Make chat work
This commit is contained in:
parent
a09543b2fb
commit
3da964f5d6
8 changed files with 195 additions and 58 deletions
|
@ -1,4 +1,5 @@
|
||||||
import { ConsoleHelper } from "../ConsoleHelper";
|
import { ConsoleHelper } from "../ConsoleHelper";
|
||||||
|
import { Channel } from "./objects/Channel";
|
||||||
import { ChatManager } from "./ChatManager";
|
import { ChatManager } from "./ChatManager";
|
||||||
import { Database } from "./objects/Database";
|
import { Database } from "./objects/Database";
|
||||||
import { LatLng } from "./objects/LatLng";
|
import { LatLng } from "./objects/LatLng";
|
||||||
|
@ -16,8 +17,6 @@ const config:any = JSON.parse(readFileSync("./config.json").toString());
|
||||||
// TODO: Port osu-packet to TypeScript
|
// TODO: Port osu-packet to TypeScript
|
||||||
const osu = require("osu-packet");
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
/*Streams = require("./Streams.js");*/
|
|
||||||
|
|
||||||
const DB:Database = new Database(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
const DB:Database = new Database(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
||||||
// Close any unclosed db matches on startup
|
// Close any unclosed db matches on startup
|
||||||
DB.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
DB.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||||
|
@ -32,7 +31,7 @@ const streams:DataStreamArray = new DataStreamArray();
|
||||||
|
|
||||||
// ChatManager
|
// ChatManager
|
||||||
const chatManager:ChatManager = new ChatManager(streams);
|
const chatManager:ChatManager = new ChatManager(streams);
|
||||||
chatManager.AddChatChannel("osu", "The main channel");
|
chatManager.AddChatChannel("osu", "The main channel", true);
|
||||||
chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff");
|
chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff");
|
||||||
chatManager.AddChatChannel("english", "Talk in exclusively English");
|
chatManager.AddChatChannel("english", "Talk in exclusively English");
|
||||||
chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese");
|
chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese");
|
||||||
|
@ -82,42 +81,25 @@ if (config.redis.enabled) {
|
||||||
})();
|
})();
|
||||||
} else ConsoleHelper.printWarn("Redis is disabled!");
|
} else ConsoleHelper.printWarn("Redis is disabled!");
|
||||||
|
|
||||||
|
// Import packets
|
||||||
|
import { ChangeAction } from "./packets/ChangeAction";
|
||||||
|
import { Logout } from "./packets/Logout";
|
||||||
|
import { UserPresence } from "./packets/UserPresence";
|
||||||
|
import { UserStatsRequest } from "./packets/UserStatsRequest";
|
||||||
|
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||||
|
|
||||||
// User timeout interval
|
// User timeout interval
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
for (let User of users.getIterableItems()) {
|
for (let User of users.getIterableItems()) {
|
||||||
if (User.uuid == "bot") continue; // Ignore the bot
|
if (User.uuid == "bot") continue; // Ignore the bot
|
||||||
|
|
||||||
// Logout this user, they're clearly gone.
|
// Logout this user, they're clearly gone.
|
||||||
// if (Date.now() >= User.timeoutTime)
|
if (Date.now() >= User.timeoutTime) {
|
||||||
// Logout(User);
|
Logout(User);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
// Include packets
|
|
||||||
/*const ChangeAction = require("./Packets/ChangeAction.js"),
|
|
||||||
SendPublicMessage = require("./Packets/SendPublicMessage.js"),
|
|
||||||
Logout = require("./Packets/Logout.js"),
|
|
||||||
Spectator = require("./Spectator.js"),
|
|
||||||
SendPrivateMessage = require("./Packets/SendPrivateMessage.js"),
|
|
||||||
MultiplayerManager = require("./MultiplayerManager.js"),
|
|
||||||
SetAwayMessage = require("./Packets/SetAwayMessage.js"),
|
|
||||||
ChannelJoin = require("./Packets/ChannelJoin.js"),
|
|
||||||
ChannelPart = require("./Packets/ChannelPart.js"),
|
|
||||||
AddFriend = require("./Packets/AddFriend.js"),
|
|
||||||
RemoveFriend = require("./Packets/RemoveFriend.js"),
|
|
||||||
UserPresenceBundle = require("./Packets/UserPresenceBundle.js"),
|
|
||||||
UserPresence = require("./Packets/UserPresence.js"),
|
|
||||||
UserStatsRequest = require("./Packets/UserStatsRequest.js"),
|
|
||||||
MultiplayerInvite = require("./Packets/MultiplayerInvite.js"),
|
|
||||||
TourneyMatchSpecialInfo = require("./Packets/TourneyMatchSpecialInfo.js"),
|
|
||||||
TourneyMatchJoinChannel = require("./Packets/TourneyMatchSpecialInfo.js"),
|
|
||||||
TourneyMatchLeaveChannel = require("./Packets/TourneyLeaveMatchChannel.js");*/
|
|
||||||
import { ChangeAction } from "./packets/ChangeAction";
|
|
||||||
import { Logout } from "./packets/Logout";
|
|
||||||
import { UserPresence } from "./packets/UserPresence";
|
|
||||||
import { UserStatsRequest } from "./packets/UserStatsRequest";
|
|
||||||
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
|
||||||
|
|
||||||
const EMPTY_BUFFER = Buffer.alloc(0);
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
||||||
|
|
||||||
export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
|
@ -163,7 +145,10 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SendPublicMessage:
|
case Packets.Client_SendPublicMessage:
|
||||||
//SendPublicMessage(PacketUser, CurrentPacket.data);
|
let channel = chatManager.GetChannelByName(CurrentPacket.data.target);
|
||||||
|
if (channel instanceof Channel) {
|
||||||
|
channel.SendMessage(PacketUser, CurrentPacket.data.message);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_Logout:
|
case Packets.Client_Logout:
|
||||||
|
|
|
@ -2,9 +2,12 @@ import { Channel } from "./objects/Channel";
|
||||||
import { ConsoleHelper } from "../ConsoleHelper";
|
import { ConsoleHelper } from "../ConsoleHelper";
|
||||||
import { FunkyArray } from "./objects/FunkyArray";
|
import { FunkyArray } from "./objects/FunkyArray";
|
||||||
import { DataStreamArray } from "./objects/DataStreamArray";
|
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||||
|
import { User } from "./objects/User";
|
||||||
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
export class ChatManager {
|
export class ChatManager {
|
||||||
public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
||||||
|
public forceJoinChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
||||||
public streams:DataStreamArray;
|
public streams:DataStreamArray;
|
||||||
|
|
||||||
public constructor(streams:DataStreamArray) {
|
public constructor(streams:DataStreamArray) {
|
||||||
|
@ -13,7 +16,48 @@ export class ChatManager {
|
||||||
|
|
||||||
public AddChatChannel(name:string, description:string, forceJoin:boolean = false) {
|
public AddChatChannel(name:string, description:string, forceJoin:boolean = false) {
|
||||||
const stream = this.streams.CreateStream(`chat_channel:${name}`, false);
|
const stream = this.streams.CreateStream(`chat_channel:${name}`, false);
|
||||||
this.chatChannels.add(name, new Channel(name, description, stream, forceJoin));
|
const channel = new Channel(`#${name}`, description, stream);
|
||||||
|
this.chatChannels.add(channel.name, channel);
|
||||||
|
if (forceJoin) {
|
||||||
|
this.forceJoinChannels.add(name, channel);
|
||||||
|
}
|
||||||
ConsoleHelper.printChat(`Created chat channel [${name}]`);
|
ConsoleHelper.printChat(`Created chat channel [${name}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RemoveChatChannel(channel:Channel | string) {
|
||||||
|
if (channel instanceof Channel) {
|
||||||
|
channel.stream.Delete();
|
||||||
|
this.chatChannels.remove(channel.stream.name);
|
||||||
|
this.forceJoinChannels.remove(channel.stream.name)
|
||||||
|
} else {
|
||||||
|
const chatChannel = this.GetChannelByName(channel);
|
||||||
|
if (chatChannel instanceof Channel) {
|
||||||
|
chatChannel.stream.Delete();
|
||||||
|
this.chatChannels.remove(chatChannel.stream.name);
|
||||||
|
this.forceJoinChannels.remove(chatChannel.stream.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetChannelByName(channelName:string) : Channel | undefined {
|
||||||
|
return this.chatChannels.getByKey(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForceJoinChannels(user:User) {
|
||||||
|
for (let channel of this.forceJoinChannels.getIterableItems()) {
|
||||||
|
channel.Join(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendChannelListing(user:User) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
for (let channel of this.chatChannels.getIterableItems()) {
|
||||||
|
osuPacketWriter.ChannelAvailable({
|
||||||
|
channelName: channel.name,
|
||||||
|
channelTopic: channel.description,
|
||||||
|
channelUserCount: channel.userCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -192,23 +192,13 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, dat
|
||||||
// peppy pls, why
|
// peppy pls, why
|
||||||
osuPacketWriter.ChannelListingComplete();
|
osuPacketWriter.ChannelListingComplete();
|
||||||
|
|
||||||
// Add user to #osu
|
// Setup chat
|
||||||
osuPacketWriter.ChannelJoinSuccess("#osu");
|
chatManager.ForceJoinChannels(newUser);
|
||||||
//if (!Streams.isUserInStream("#osu", newUser.uuid))
|
chatManager.SendChannelListing(newUser);
|
||||||
// Streams.addUserToStream("#osu", newUser.uuid);
|
|
||||||
|
|
||||||
// List all channels out to the client
|
|
||||||
/*for (let i = 0; i < global.channels.length; i++) {
|
|
||||||
osuPacketWriter.ChannelAvailable({
|
|
||||||
channelName: global.channels[i].channelName,
|
|
||||||
channelTopic: global.channels[i].channelTopic,
|
|
||||||
channelUserCount: global.channels[i].channelUserCount
|
|
||||||
});
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Construct user's friends list
|
// Construct user's friends list
|
||||||
const userFriends = await database.query("SELECT friendsWith FROM friends WHERE user = ?", [newUser.id]);
|
const userFriends = await database.query("SELECT friendsWith FROM friends WHERE user = ?", [newUser.id]);
|
||||||
let friendsArray = new Array<number>;
|
const friendsArray:Array<number> = new Array<number>();
|
||||||
for (let i = 0; i < userFriends.length; i++) {
|
for (let i = 0; i < userFriends.length; i++) {
|
||||||
friendsArray.push(userFriends[i].friendsWith);
|
friendsArray.push(userFriends[i].friendsWith);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,3 +24,17 @@ export function generateSession() : Promise<string> {
|
||||||
export function generateSessionSync() : string {
|
export function generateSessionSync() : string {
|
||||||
return randomBytes(12).toString("hex");
|
return randomBytes(12).toString("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hexlify(data:Buffer) : string {
|
||||||
|
let out:string = "";
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const hex = data[i].toString(16);
|
||||||
|
if (hex.length === 1) {
|
||||||
|
out += `0${hex.toUpperCase()},`;
|
||||||
|
} else {
|
||||||
|
out += `${hex.toUpperCase()},`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.slice(0, out.length - 1);
|
||||||
|
}
|
|
@ -1,20 +1,70 @@
|
||||||
import { DataStream } from "./DataStream";
|
import { DataStream } from "./DataStream";
|
||||||
|
import { User } from "./User";
|
||||||
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
export class Channel {
|
export class Channel {
|
||||||
public name:string;
|
public name:string;
|
||||||
public description:string;
|
public description:string;
|
||||||
public userCount:number = 0;
|
public stream:DataStream;
|
||||||
private stream:DataStream;
|
private isLocked:boolean = false;
|
||||||
private forceJoin:boolean;
|
|
||||||
|
|
||||||
public constructor(name:string, description:string, stream:DataStream, forceJoin:boolean = false) {
|
public constructor(name:string, description:string, stream:DataStream) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.forceJoin = forceJoin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendMessage(message:string) {
|
public get userCount() {
|
||||||
|
return this.stream.userCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendMessage(sender:User, message:string) {
|
||||||
|
const isBotCommand = message[0] === "!";
|
||||||
|
|
||||||
|
if (this.isLocked && !isBotCommand) {
|
||||||
|
return this.SendSystemMessage("This channel is currently locked", sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBotCommand) {
|
||||||
|
if (message.split(" ")[0] === "!lock") {
|
||||||
|
this.isLocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
osuPacketWriter.SendMessage({
|
||||||
|
sendingClient: sender.username,
|
||||||
|
message: message,
|
||||||
|
target: this.name,
|
||||||
|
senderId: sender.id
|
||||||
|
});
|
||||||
|
this.stream.SendWithExclusion(osuPacketWriter.toBuffer, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendSystemMessage(message:string, sendTo?:User) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
osuPacketWriter.SendMessage({
|
||||||
|
sendingClient: "System",
|
||||||
|
message: message,
|
||||||
|
target: this.name,
|
||||||
|
senderId: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sendTo instanceof User) {
|
||||||
|
sendTo.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
} else {
|
||||||
|
this.stream.Send(osuPacketWriter.toBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Join(user:User) {
|
||||||
|
this.stream.AddUser(user);
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
osuPacketWriter.ChannelJoinSuccess(this.name);
|
||||||
|
user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Leave(user:User) {
|
||||||
|
this.stream.RemoveUser(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,12 +3,14 @@ import { Constants } from "../../Constants";
|
||||||
import { DataStreamArray } from "./DataStreamArray";
|
import { DataStreamArray } from "./DataStreamArray";
|
||||||
import { User } from "./User";
|
import { User } from "./User";
|
||||||
import { UserArray } from "./UserArray";
|
import { UserArray } from "./UserArray";
|
||||||
|
import { hexlify } from "../Util";
|
||||||
|
|
||||||
export class DataStream {
|
export class DataStream {
|
||||||
private users:UserArray = new UserArray();
|
private users:UserArray = new UserArray();
|
||||||
private readonly name:string;
|
public readonly name:string;
|
||||||
private readonly parent:DataStreamArray;
|
private readonly parent:DataStreamArray;
|
||||||
private readonly removeWhenEmpty:boolean;
|
private readonly removeWhenEmpty:boolean;
|
||||||
|
private inactive:boolean = false;
|
||||||
|
|
||||||
public constructor(name:string, parent:DataStreamArray, removeWhenEmpty:boolean) {
|
public constructor(name:string, parent:DataStreamArray, removeWhenEmpty:boolean) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -16,30 +18,66 @@ export class DataStream {
|
||||||
this.removeWhenEmpty = removeWhenEmpty;
|
this.removeWhenEmpty = removeWhenEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkInactive() {
|
||||||
|
if (this.inactive) {
|
||||||
|
throw `Stream ${this.name} is inactive (deleted) and cannot be used here.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get userCount() : number {
|
||||||
|
return this.users.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
public AddUser(user:User) : void {
|
public AddUser(user:User) : void {
|
||||||
|
this.checkInactive();
|
||||||
|
|
||||||
if (!(user.uuid in this.users.getItems())) {
|
if (!(user.uuid in this.users.getItems())) {
|
||||||
this.users.add(user.uuid, user);
|
this.users.add(user.uuid, user);
|
||||||
ConsoleHelper.printStream(`Added user [${user.username}|${user.uuid}] to stream [${this.name}]`);
|
ConsoleHelper.printStream(`Added [${user.username}] to stream [${this.name}]`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoveUser(user:User) : void {
|
public RemoveUser(user:User) : void {
|
||||||
|
this.checkInactive();
|
||||||
|
|
||||||
if (user.uuid in this.users.getItems()) {
|
if (user.uuid in this.users.getItems()) {
|
||||||
this.users.remove(user.uuid);
|
this.users.remove(user.uuid);
|
||||||
ConsoleHelper.printStream(`Removed user [${user.username}|${user.uuid}] from stream [${this.name}]`);
|
ConsoleHelper.printStream(`Removed [${user.username}] from stream [${this.name}]`);
|
||||||
}
|
}
|
||||||
if (this.removeWhenEmpty && this.users.getLength() === 0) {
|
if (this.removeWhenEmpty && this.users.getLength() === 0) {
|
||||||
this.parent.remove(this.name);
|
this.Delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Delete() {
|
||||||
|
this.parent.DeleteStream(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Deactivate() {
|
||||||
|
this.inactive = true;
|
||||||
|
}
|
||||||
|
|
||||||
public Send(data:Buffer) {
|
public Send(data:Buffer) {
|
||||||
|
this.checkInactive();
|
||||||
|
|
||||||
for (let user of this.users.getIterableItems()) {
|
for (let user of this.users.getIterableItems()) {
|
||||||
user.addActionToQueue(data);
|
user.addActionToQueue(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Constants.DEBUG) {
|
if (Constants.DEBUG) {
|
||||||
ConsoleHelper.printStream(`Sent [${data.toString()}] to all users in stream [${this.name}]`);
|
ConsoleHelper.printStream(`Sent [${data.toString()}] to all users in stream [${this.name}]`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SendWithExclusion(data:Buffer, exclude:User) {
|
||||||
|
this.checkInactive();
|
||||||
|
|
||||||
|
for (let user of this.users.getIterableItems()) {
|
||||||
|
if (user.uuid !== exclude.uuid) {
|
||||||
|
user.addActionToQueue(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Constants.DEBUG) {
|
||||||
|
ConsoleHelper.printStream(`Sent Buffer<${hexlify(data)}> to all users in stream [${this.name}] excluding user [${exclude.username}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,21 @@ export class DataStreamArray extends FunkyArray<DataStream> {
|
||||||
return dataStream;
|
return dataStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeleteStream(stream:DataStream | string) {
|
||||||
|
const isObject = stream instanceof DataStream;
|
||||||
|
if (isObject) {
|
||||||
|
stream.Deactivate();
|
||||||
|
this.remove(stream.name);
|
||||||
|
} else {
|
||||||
|
const dso = this.getByKey(stream);
|
||||||
|
if (dso != null) {
|
||||||
|
dso.Deactivate();
|
||||||
|
}
|
||||||
|
this.remove(stream);
|
||||||
|
}
|
||||||
|
ConsoleHelper.printStream(`Deleted stream [${isObject ? stream.name : stream}]`);
|
||||||
|
}
|
||||||
|
|
||||||
public RemoveUserFromAllStreams(user:User) {
|
public RemoveUserFromAllStreams(user:User) {
|
||||||
for (let stream of this.getIterableItems()) {
|
for (let stream of this.getIterableItems()) {
|
||||||
stream.RemoveUser(user);
|
stream.RemoveUser(user);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { DataStream } from "./DataStream";
|
||||||
import { UserArray } from "./UserArray";
|
import { UserArray } from "./UserArray";
|
||||||
import { DataStreamArray } from "./DataStreamArray";
|
import { DataStreamArray } from "./DataStreamArray";
|
||||||
import { ChatManager } from "../ChatManager";
|
import { ChatManager } from "../ChatManager";
|
||||||
|
import { StatusUpdate } from "../packets/StatusUpdate";
|
||||||
//const StatusUpdate = require("./Packets/StatusUpdate.js");
|
//const StatusUpdate = require("./Packets/StatusUpdate.js");
|
||||||
|
|
||||||
const rankingModes = [
|
const rankingModes = [
|
||||||
|
@ -146,7 +147,7 @@ export class User {
|
||||||
else this.pp = 0;
|
else this.pp = 0;
|
||||||
|
|
||||||
if (userScoreUpdate || forceUpdate) {
|
if (userScoreUpdate || forceUpdate) {
|
||||||
//StatusUpdate(this, this.id);
|
StatusUpdate(this, this.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue