//      public override bool SaveFiles()
//      {
//         if(MyExtensions.MySerialize.SaveObject<List
//      }

      public override List<JSONObject> ProcessCommand(UserCommand command, UserInfo user, Dictionary<int, UserInfo> users)
      {
         List<JSONObject> outputs = new List<JSONObject>();

         if (command.Command == "drawsubmit")
         {
            //unsavedMessages.Add(new DrawingInfo() { Drawing = command.Arguments[0], User = user, postTime = DateTime.Now });
            UserMessageJSONObject drawingMessage = new UserMessageJSONObject(user, command.Arguments[0], command.tag);
            drawingMessage.encoding = Nickname;
            drawingMessage.spamValue = 0.50;
            drawingMessage.SetUnspammable();
            outputs.Add(drawingMessage);
         }

         return outputs; //base.ProcessCommand(command, user, users);
      }
//      public void SimpleEnter(int uid)
//      {
//         BroadcastUserList();
//
//         //Only perform special leaving messages and processing if the user was real
//         if (users.ContainsKey(uid))
//         {
//            if (!users[uid].PerformOnChatEnter())
//               Logger.Warning("User session timer was in an invalid state!");
//
//            if (users[uid].ShowMessages)
//               Broadcast(new LanguageTagParameters(ChatTags.Join, users[uid]), new SystemMessageJSONObject());
//         }
//      }

//      public void SimpleLeave(int uid)
//      {
//         BroadcastUserList();
//
//         //Only perform special leaving messages and processing if the user was real
//         if (users.ContainsKey(uid))
//         {
//            if (!users[uid].PerformOnChatLeave())
//               Logger.Warning("User session timer was in an invalid state!");
//
//            if (users[uid].ShowMessages)
//               Broadcast(new LanguageTagParameters(ChatTags.Leave, users[uid]), new SystemMessageJSONObject());
//         }
//      }

      /*public void IrcRelayJoin(string username)
      {
         Log("IRC Join: " + username);
         SimpleEnter(UserLookup(username));
      }

      public void IrcRelayLeave(string username)
      {
         Log("IRC Leave: " + username);
         SimpleLeave(UserLookup(username));
      }

      public void IrcRelayMessage(string sender, string message)
      {
         //Skip this buttneck
         if (IrcSkipUsers().Contains(sender))
            return;
         
         User ircUser = GetUser(UserLookup(sender));

         if (!ircUser.IrcUser || ircUser.UID <= 0)
            return;

         UserMessageJSONObject ircMessage = new UserMessageJSONObject(ircUser, System.Security.SecurityElement.Escape(message), IrcTag);

         AddMessage(ircMessage);
         BroadcastMessageList();
      }*/

      public ChatTags AddMessage(UserMessageJSONObject message)
      {
         ChatTags warning = ChatTags.None;

         User user = GetUser(message.uid);

         //Update spam score
         if(message.Spammable)
         {
            lock(managerLock)
            {
               Log("Enter messagespam lock", MyExtensions.Logging.LogLevel.Locks);
               warning = user.MessageSpam(messages, message.message);
               Log("Exit messagespam lock", MyExtensions.Logging.LogLevel.Locks);
            }
         }

         //Only add message to message list if we previously set that we should.
         if(user.BlockedUntil < DateTime.Now)
         {
            lock(managerLock)
            {
               //First add to history
               if (!history.ContainsKey(message.tag))
                  history.Add(message.tag, new List<UserMessageJSONObject>());
               if(message.Display)
                  history[message.tag].Add(message);

               //Then, get rid of history we don't need anymore
               foreach(string key in history.Keys.ToList())
                  history[key] = history[key].Where(x => (DateTime.Now - x.PostTime()).TotalDays <= 1).OrderByDescending(x => x.PostTime()).Take(ChatSettings.MaxMessageSend).ToList();
               history = history.Where(x => ChatSettings.AcceptedTags.Contains(x.Key) || rooms.ContainsKey(x.Key)).ToDictionary(x => x.Key, y => y.Value);

               Log("Enter message add lock", MyExtensions.Logging.LogLevel.Locks);
               messages.Add(message);
               messages = messages.Skip(Math.Max(0, messages.Count() - ChatSettings.MaxMessageKeep)).ToList();

               if (rooms.ContainsKey(message.tag))
                  rooms[message.tag].OnMessage();
               
               Log("Exit message add lock", MyExtensions.Logging.LogLevel.Locks);
            }
            user.PerformOnPost();
         }

         return warning;
      }
 //Most modules will not need this. This function is called by the chat server whenever it recieves a message 
 //(any message, including non commands). Unless you're performing analysis or saving information on each message
 //(like this statistics class), you won't need to use this. Notice that it does not return any information; you
 //cannot give output to the chat server here. Also notice the "Log" function call here. This is a wrapper provided
 //by Module which lets you write to the chat server log. There's a whole system in place for creating your own logs,
 //but you probably just want to write to the main log. You can also specify the level of the message, which defaults
 //to "Normal". If the message is unimportant, you probably want "Debug".
 public override void ProcessMessage(UserMessageJSONObject message, UserInfo user, Dictionary<int, UserInfo> users)
 {
    //Add user to statistics dictionary if they don't already exist.
    if (!userStatistics.ContainsKey(user.UID))
    {
       userStatistics.Add(user.UID, new UserStatistics());
       Log("Added new user: " + user.Username);
    }
       
    if (message.Display)
    {
       userStatistics[user.UID].AddMessage(message.message);
       userStatistics[user.UID].AddUsers(users.Where(x => x.Value.LoggedIn).Select(x => x.Value.UID).ToList());
    }
 }
 public UserMessageJSONObject(UserMessageJSONObject copy) : base(copy)
 {
    if (copy != null)
    {
       username = copy.username;
       avatar = copy.avatar;
       stars = copy.stars;
       uid = copy.uid;
       message = copy.message;
       id = copy.id;
       //tag = copy.tag;
       postTime = copy.postTime;
    }
 }
Esempio n. 5
0
      /// <summary>
      /// Parse and build command if possible. Assign module which will handle command. 
      /// </summary>
      /// <returns>Successful command parse</returns>
      /// <param name="message">Message.</param>
      /// <param name="commandModule">Command module.</param>
      /// <param name="userCommand">User command.</param>
      private bool TryCommandParse(UserMessageJSONObject message, out Module commandModule, out UserCommand userCommand, out string error)
      {
         userCommand = null;
         commandModule = null;
         error = "";

         UserCommand tempUserCommand = null;
         List<Module> modules = manager.GetModuleListCopy(UID);

         string realMessage = System.Net.WebUtility.HtmlDecode(message.message);

         //Check through all modules for possible command match
         foreach(Module module in modules)
         {
            //We already found the module, so get out.
            if(commandModule != null)
               break;

            //Check through this module's command for possible match
            foreach(ModuleCommand command in module.Commands)
            {
               Match match = Regex.Match(realMessage, command.FullRegex, RegexOptions.Singleline);
               //Match partialMatch = Regex.Match(message.message, command.CommandRegex, RegexOptions.Singleline);

               //This command matched, so preparse the command and get out of here.
               if (match.Success)
               {
                  //Build arguments from regex.
                  List<string> arguments = new List<string>();
                  for (int i = 2; i < match.Groups.Count; i++)
                     arguments.Add(match.Groups[i].Value.Trim());

                  //We have a user command. Cool, but will it parse? Ehhhh.
                  tempUserCommand = new UserCommand(match.Groups[1].Value, arguments, message, command);

                  //Now preprocess the command to make sure certain standard fields check out (like username)
                  for (int i = 0; i < command.Arguments.Count; i++)
                  {
                     //Users need to exist. If not, throw error.
                     if (command.Arguments[i].Type == ArgumentType.User)
                     {
                        tempUserCommand.Arguments[i] = ParseUser(tempUserCommand.Arguments[i]);

                        for (int j = 0; j < tempUserCommand.ArgumentParts[i].Count; j++)
                           tempUserCommand.ArgumentParts[i][j] = ParseUser(tempUserCommand.ArgumentParts[i][j]);

                        if (!((tempUserCommand.MatchedCommand.Arguments[i].Repeat == RepeatType.ZeroOrOne ||
                               tempUserCommand.MatchedCommand.Arguments[i].Repeat == RepeatType.ZeroOrMore) &&
                              string.IsNullOrWhiteSpace(tempUserCommand.Arguments[i])) && 
                           (tempUserCommand.ArgumentParts[i].Count == 0 && manager.UserLookup(tempUserCommand.Arguments[i]) < 0 ||
                              tempUserCommand.ArgumentParts[i].Any(x => manager.UserLookup(x) < 0)))
                        {
                           error = "User does not exist";
                           return false;
                        }
                     }
                     else if (command.Arguments[i].Type == ArgumentType.Module)
                     {
                        if (!modules.Any(x => x.Nickname == arguments[i].ToLower()))
                        {
                           error = "Module does not exist";
                           return false;
                        }
                     }
                  }

                  commandModule = module;
                  break;
               }
            }
         }

         if (commandModule == null && ModuleCommand.IsACommand(message.message))
         {
            //OOPS! A command was parsed but it wasn't parsed correctly. Doop
            error = "\"" + message.message + "\" was not recognized. Maybe something was misspelled, or you were missing arguments";
            return false;
         }

         userCommand = new UserCommand(tempUserCommand);
         return (commandModule != null);
      }
Esempio n. 6
0
//      protected override void OnError(ErrorEventArgs e)
//      {
//         if(e.Message != null)
//            Logger.Error("UID: " + uid + " - " + e.Message, "WebSocket");
//         if(e.Exception != null)
//            Logger.Error(e.Exception.ToString(), "WebSocket");
//
//         //OK let's see what happens here
//         //OnClose(null);
//         //base.OnError(e);
//      }

      //I guess this is WHENEVER it receives a message?
      public override void ReceivedMessage(string rawMessage)
      {
         //Log("Got message: " + rawMessage, MyExtensions.Logging.LogLevel.Debug);

         ResponseJSONObject response = new ResponseJSONObject();
         response.result = false;
         dynamic json = new Object();
         string type = "";

         //You HAVE to do this, even though it seems pointless. Users need to show up as banned immediately.
         ThisUser.PullInfoFromQueryPage();

         //Before anything else, log the amount of incoming data
         if (!string.IsNullOrEmpty(rawMessage))
         {
            manager.Bandwidth.AddIncoming(rawMessage.Length + HeaderSize);
         }

         //First, just try to parse the JSON they gave us. If it's absolute
         //garbage (or just not JSON), let them know and quit immediately.
         try
         {
            json = JsonConvert.DeserializeObject(rawMessage);
            type = json.type;
            response.from = type;
         }
         catch
         {
            response.errors.Add("Could not parse JSON");
         }

         //If we got a bind message, let's try to authorize this channel.
         if (type == "bind")
         {
            if (uid > 0)
            {
               response.errors.Add("Received another bind message, but you've already been authenticated.");
            }
            else
            {
               try
               {
                  //First, gather information from the JSON. This is so that if
                  //the json is invalid, it will fail as soon as possible
                  string key = (string)json.key;
                  int newUser = (int)json.uid;

                  //Oops, username was invalid
                  if (newUser <= 0)
                  {
                     Log("Tried to bind a bad UID: " + newUser);
                     response.errors.Add("UID was invalid");
                  }
                  else
                  {
                     //List<Chat> removals;
                     string error;

                     if (!manager.CheckAuthentication(newUser, key, out error))  //newUser, key, out removals, out error))
                     {
                        response.errors.Add(error);
                     }
                     else
                     {
                        //Before we do anything, remove other chatting sessions
                        foreach (Chat removeChat in GetAllUsers().Select(x => (Chat)x).Where(x => x.UID == newUser && x != this))
                           removeChat.CloseSelf();
                        
                           //Sessions.CloseSession(removeChat.ID);
                     
                        uid = newUser;

                        //BEFORE adding, broadcast the "whatever has entered the chat" message
                        if (ThisUser.ShowMessages)
                        {
                           manager.Broadcast(QuickParams(ChatTags.Join), new SystemMessageJSONObject(),
                              new List<Chat> { this });
                        }
                        
                        MySend(NewSystemMessageFromTag(QuickParams(ChatTags.Welcome)).ToString());
                        ChatTags enterSpamWarning = ThisUser.JoinSpam();

                        if (enterSpamWarning != ChatTags.None)
                           MySend(NewWarningFromTag(QuickParams(enterSpamWarning)).ToString());

                        //BEFORE sending out the user list, we need to perform onPing so that it looks like this user is active
                        sessionID = ThisUser.PerformOnChatEnter();
//                        if (!ThisUser.PerformOnChatEnter())
//                           Log("Invalid session entry. Sessions may be broken", LogLevel.Warning);

                        manager.BroadcastUserList();

                        Log("Authentication complete: UID " + uid + " maps to username " + ThisUser.Username +
                           (ThisUser.CanStaffChat ? "(staff)" : ""));
                        response.result = true;

                        List<JSONObject> outputs = new List<JSONObject>();
                        Dictionary<int, UserInfo> currentUsers = manager.UsersForModules();

                        //Also do some other crap
                        foreach (Module module in manager.GetModuleListCopy())
                        {
                           if (Monitor.TryEnter(module.Lock, manager.ChatSettings.MaxModuleWait))
                           {
                              try
                              {
                                 outputs.AddRange(module.OnUserJoin(currentUsers[ThisUser.UID], currentUsers));
                              }
                              finally
                              {
                                 Monitor.Exit(module.Lock);
                              }
                           }
                           else
                           {
                              Log("Skipped " + module.ModuleName + " join processing", 
                                 MyExtensions.Logging.LogLevel.Warning);
                           }
                        }

                        OutputMessages(outputs, ThisUser.UID);

                        //Finally, output the "Yo accept dis" thing if they haven't already.
                        if(!ThisUser.AcceptedPolicy)
                        {
                           MessageListJSONObject emptyMessages = new MessageListJSONObject();
                           UserListJSONObject emptyUsers = new UserListJSONObject();
                           ModuleJSONObject policy = new ModuleJSONObject(ChatServer.Policy);
                           ModuleJSONObject accept = new ModuleJSONObject("\nYou must accept this chat policy before " +
                              "using the chat. Type /accept if you accept the chat policy\n");
                           MySend(emptyMessages.ToString(), true);
                           MySend(emptyUsers.ToString(), true);
                           MySend(policy.ToString(), true);
                           MySend(accept.ToString(), true);
                        }
                        else if(ThisUser.ShouldPolicyRemind)
                        {
                           ModuleJSONObject policy = new ModuleJSONObject(ChatServer.Policy);
                           MySend(policy.ToString());
                           ThisUser.PerformOnReminder();
                        }

                        //Now set up the IRC relay. Oh boy, let's hope this works!
                        /*relay = new SimpleIRCRelay(manager.IrcServer, manager.IrcChannel, ThisUser.Username, Logger);
                        relay.ConnectAsync();*/
                        //relay.IRCRelayMessageEvent += OnIrcRelayMessage;
                     }
                  }
               }
               catch
               {
                  response.errors.Add("BIND message was missing fields");
               }
            }
         }
         else if (type == "ping")
         {
            lastPing = DateTime.Now;

            bool active = true;

            try
            {
               active = (bool)json.active;
               //relay.Ping();
            }
            catch (Exception messageError)
            {
               response.errors.Add("Internal server error: " + messageError/*.Message*/);
            }

            ThisUser.PerformOnPing(active);
            UpdateActiveUserList(null, null);
         }
         else if (type == "message")
         {
            try
            {
               //First, gather information from the JSON. This is so that if
               //the json is invalid, it will fail as soon as possible
               string key = json.key;
               string message = System.Security.SecurityElement.Escape((string)json.text);
               string tag = json.tag;

               //These first things don't increase spam score in any way
               if (string.IsNullOrWhiteSpace(message))
               {
                  response.errors.Add("No empty messages please");
               }
               else if (!manager.CheckKey(uid, key))
               {
                  Log("Got invalid key " + key + " from " + UserLogString);
                  response.errors.Add("Your key is invalid");
               }
               else if (!ThisUser.AcceptedPolicy)
               {
                  if(message != "/accept")
                  {
                     response.errors.Add("The only command available right now is /accept");
                  }
                  else
                  {
                     ModuleJSONObject acceptSuccess = new ModuleJSONObject("You have accepted the SmileBASIC Source " +
                        "chat policy. Please use the appropriate chat tab for discussion about SmileBASIC or off-topic " +
                        "subjects!");
                     MySend(acceptSuccess.ToString(), true);

                     Thread.Sleep(2000);
                     ThisUser.AcceptPolicy();
                     ThisUser.PerformOnReminder();
                     response.result = true;

                     MySend(manager.ChatUserList(UID));
                     MySend(manager.ChatMessageList(UID));
                  }
               }
               else if (ThisUser.Blocked)
               {
                  response.errors.Add(NewWarningFromTag(QuickParams(ChatTags.Blocked)).message);
               }
               else if (ThisUser.Banned)
               {
                  response.errors.Add("You are banned from chat for " +
                  StringExtensions.LargestTime(ThisUser.BannedUntil - DateTime.Now));
               }
               else if (tag == "admin" && !ThisUser.CanStaffChat ||
                        tag == manager.ChatSettings.GlobalTag && !ThisUser.CanGlobalChat)
               {
                  response.errors.Add("You can't post messages here. I'm sorry.");
               }
               else if (!manager.ValidTagForUser(UID, tag))
               {
                  response.errors.Add("Your post has an unrecognized tag. Cannot display");
               }
               else
               {
                  Dictionary<int, UserInfo> currentUsers = manager.UsersForModules();
                  List<JSONObject> outputs = new List<JSONObject>();
                  UserMessageJSONObject userMessage = new UserMessageJSONObject(ThisUser, message, tag);
                  UserCommand userCommand;
                  Module commandModule;
                  string commandError = "";

                  //Step 1: parse a possible command. If no command is parsed, no module will be written.
                  if (TryCommandParse(userMessage, out commandModule, out userCommand, out commandError))
                  {
                     Log("Trying to use module " + commandModule.ModuleName + " to process command " + 
                        userCommand.message + " from " + ThisUser.Username, MyExtensions.Logging.LogLevel.SuperDebug);

                     //We found a command. Send it off to the proper module and get the output
                     if (Monitor.TryEnter(commandModule.Lock, manager.ChatSettings.MaxModuleWait))
                     {
                        try
                        {
                           outputs.AddRange(commandModule.ProcessCommand(userCommand, currentUsers[ThisUser.UID], currentUsers));
                        }
                        finally
                        {
                           Monitor.Exit(commandModule.Lock);
                        }
                     }
                     else
                     {
                        response.errors.Add("The chat server is busy and can't process your command right now");
                        userMessage.SetHidden();
                        userMessage.SetUnspammable();
                     }

                     //do not update spam score if command module doesn't want it
                     if (!userCommand.MatchedCommand.ShouldUpdateSpamScore)
                        userMessage.SetUnspammable();

                     //For now, simply capture all commands no matter what.
                     userMessage.SetHidden();
                     //userMessage.SetCommand();

                     Log("Module " + commandModule.ModuleName + " processed command from " + UserLogString, 
                        MyExtensions.Logging.LogLevel.Debug);
                  }
                  else
                  {
                     //If an error was given, add it to our response
                     if (!string.IsNullOrWhiteSpace(commandError))
                     {
                        response.errors.Add("Command error: " + commandError);
                        userMessage.SetHidden();
                        userMessage.SetUnspammable();
                     }
                  }

                  ChatTags warning = manager.AddMessage(userMessage);

                  //Send off on relay
                  /*if(userMessage.Display && userMessage.tag == manager.IrcTag)
                  {
                     if(relay.SendMessage(userMessage.message))
                        Logger.LogGeneral("Sent message on IRC relay!", MyExtensions.Logging.LogLevel.SuperDebug);
                     else
                        Logger.LogGeneral("Couldn't send on IRC relay!", MyExtensions.Logging.LogLevel.SuperDebug);
                  }*/

                  if (warning != ChatTags.None)
                     outputs.Add(NewWarningFromTag(QuickParams(warning)));

                  response.result = response.errors.Count == 0;

                  //Now send out userlist if active status changed
                  UpdateActiveUserList(null, null);

                  //Since we added a new message, we need to broadcast.
                  if (response.result && userMessage.Display)
                     manager.BroadcastMessageList();        //CRASH ALMOST CERTAINLY HAPPENS HERE!!!!!!!###$$$$$!!!!!!!!!!*$*$*$*

                  //Step 2: run regular message through all modules' regular message processor (probably no output?)
                  if(manager.ChatSettings.AcceptedTags.Contains(userMessage.tag))
                  {
                     foreach (Module module in manager.GetModuleListCopy())
                     {
                        if (Monitor.TryEnter(module.Lock, manager.ChatSettings.MaxModuleWait))
                        {
                           try
                           {
                              module.ProcessMessage(userMessage, currentUsers[ThisUser.UID], currentUsers);
                           }
                           finally
                           {
                              Monitor.Exit(module.Lock);
                           }
                        }
                        else
                        {
                           Log("Skipped " + module.ModuleName + " message processing", 
                              MyExtensions.Logging.LogLevel.Warning);
                        }
                     }
                  }

                  //Step 3: run all modules' post processor (no message required)
                  //Is this even necessary? It was necessary before because the bot ran on a timer. Without a timer,
                  //each module can just specify that it wants to do things at random points with its own timer.

                  //Step 4: iterate over returned messages and send them out appropriately
                  OutputMessages(outputs, ThisUser.UID, tag);
                  //end of regular message processing
               }
            }
            catch (Exception messageError)
            {
               response.errors.Add("Internal server error: " + messageError/*.Message*/);
               //response.errors.Add("Message was missing fields");
            }
         }
//         else if (type == "createroom")
//         {
//            try
//            {
//               List<int> users = json.users.ToObject<List<int>>();
//               string error;
//
//               if(!manager.CreatePMRoom(new HashSet<int>(users), ThisUser.UID, out error))
//               {
//                  response.errors.Add(error);
//               }
//               else
//               {
//                  MySend((new SystemMessageJSONObject("You created a chat room for " + string.Join(", ", users.Select(x => manager.GetUser(x).Username)))).ToString());
//                  manager.BroadcastUserList();
//               }
//            }
//            catch
//            {
//               response.errors.Add("Could not parse PM creation room message");
//            }
//         }
         else if (type == "request")
         {
            try
            {
               string wanted = json.request;

               if(wanted == "userList")
               {
                  MySend(manager.ChatUserList(UID));
                  response.result = true;
               }
               else if (wanted == "messageList")
               {
                  MySend(manager.ChatMessageList(UID));
                  response.result = true;
               }
               else
               {
                  response.errors.Add("Invalid request field");
               }
            }
            catch
            {
               response.errors.Add("Request was missing fields");
            }
         }

         //Send the "OK" message back.
         MySend(response.ToString(), true);
      }
 private void ProcessMessage(UserMessageJSONObject message, Dictionary<int, UserInfo> currentUsers)
 {
    //Step 2: run regular message through all modules' regular message processor (probably no output?)
    if (manager.ChatSettings.AcceptedTags.Contains(message.tag))
    {
       foreach (Module module in manager.GetModuleListCopy().Where(x => x.DoesProcessMessage))
       {
          if (Monitor.TryEnter(module.Lock, manager.ChatSettings.MaxModuleWait))
          {
             try
             {
                module.ProcessMessage(message, currentUsers[ThisUser.UID], currentUsers);
             }
             finally
             {
                Monitor.Exit(module.Lock);
             }
          }
          else
          {
             Log("Skipped " + module.ModuleName + " message processing", 
                MyExtensions.Logging.LogLevel.Warning);
          }
       }
    }
 }
 public override void ProcessMessage(UserMessageJSONObject message, UserInfo user, Dictionary<int, UserInfo> users)
 {
    if(message.Display)
       unsavedMessages.Add(message);
 }