[RELEASE] [PlusEMU] View PM Command

JynX

Posting Freak
Feb 6, 2016
710
438
Hello DevBest, I had a few people ask me if I had a command to add the ability to view a user's PM so I decided to code this and just release it to the public for anyone and everyone to use. This doesn't require hardly ANY knowledge of C# Note: You need to actually have a brain to add this..

It looks like this:
67c20c6396e842de8469f60152b52772.png


Anyways, here's the code for it (Add this to Plus.HabboHotel.Rooms.Chat.Commands.Moderator):
Code:
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Data;
using Plus.HabboHotel.GameClients;
using Plus.HabboHotel.Rooms;
using Plus.Communication.Packets.Outgoing.Users;
using Plus.Communication.Packets.Outgoing.Rooms.Avatar;
using System.Threading;
using System.Threading.Tasks;
using Plus.Communication.Packets.Outgoing.Rooms.Chat;
using Plus.HabboHotel.Items;
using Plus.HabboHotel.Catalog;
using Plus.Communication.Packets.Outgoing.Inventory.Furni;
using Plus.Database.Interfaces;
using Plus.Communication.Packets.Outgoing.Notifications;
using Plus.Communication.Packets.Outgoing.Rooms.Engine;

namespace Plus.HabboHotel.Rooms.Chat.Commands.Moderator
{
    class ViewPrivateMessageCommand : IChatCommand
    {
        public string PermissionRequired
        {
            get { return "command_view_pms"; }
        }

        public string Parameters
        {
            get { return "%username%"; }
        }

        public string Description
        {
            get { return "Lets you view recent PMs from and to a user."; }
        }

        public void Execute(GameClients.GameClient Session, Rooms.Room Room, string[] Params)
        {
            if (Params.Length == 1)
            {
                Session.SendWhisper("Please enter the username of the user you wish to view private messages of.");
                return;
            }

            GameClient TargetClient = PlusEnvironment.GetGame().GetClientManager().GetClientByUsername(Params[1]);
            if (TargetClient == null)
            {
                Session.SendWhisper("An error occoured whilst finding that user, maybe they're not online.");
                return;
            }

            if (TargetClient.GetHabbo() == null)
            {
                Session.SendWhisper("An error occoured whilst finding that user, maybe they're not online.");
                return;
            }
            StringBuilder b = new StringBuilder();
            b.Append("Private messages - " + TargetClient.GetHabbo().Username + "\n\n");

            using (IQueryAdapter db = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
            {
                db.SetQuery("SELECT * FROM `chatlogs_console` WHERE `from_id` = @id OR `to_id` = @id ORDER BY id DESC LIMIT 100");
                db.AddParameter("id", TargetClient.GetHabbo().Id);
                db.RunQuery();
                DataTable t = db.getTable();

                foreach (DataRow r in t.Rows)
                {
                    b.Append(UnixTimeStampToDateTime((double)PlusEnvironment.GetUnixTimestamp()) + "\n");
                    b.Append(getUsernameFromId(Convert.ToInt32(r["from_id"])) + " to " + getUsernameFromId(Convert.ToInt32(r["to_id"])) + ": " + Convert.ToString(r["message"]) + "\n\n");
                }
            }

            Session.SendMessage(new MOTDNotificationComposer(b.ToString()));
        }

        private DateTime UnixTimeStampToDateTime(double unixTimeStamp)
        {
            // Unix timestamp is seconds past epoch
            System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
            dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
            return dtDateTime;
        }

        private string getUsernameFromId(int userId)
        {
            if (PlusEnvironment.GetHabboById(userId) != null)
                return PlusEnvironment.GetHabboById(userId).Username;

            using (IQueryAdapter db = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
            {
                db.SetQuery("SELECT `username` FROM `users` WHERE `id` = @id");
                db.AddParameter("id", userId);
                db.RunQuery();
                return db.getString();
            }
        }
    }
}

Then open up your command manager (CommandManager.cs) and find this:
Code:
this.Register("ipban", new IPBanCommand());
Under that add this:
Code:
this.Register("viewpm", new ViewPrivateMessageCommand());

Then run this query in your database (replace the STAFF ID with the minimum staff rank allowed to have this command):
Code:
INSERT INTO `permissions_commands` VALUES ('command_view_pms', 'STAFF ID', '0');


There you go, the solution and you're good to go.
Your welcome,
Jake Adams.

Edit: Added a screenshot.
 
Last edited:

NeedForSpreed

Member
May 18, 2014
326
71
Thanks alot Jynx. I can't really test this but i looks great and will surely help a few people out. Thanks for releasing and contributing with something cool to the community. :)

Skickat från min FRD-L09 via Tapatalk
 

BigG

New Member
Oct 9, 2016
8
0
Very nice command.
But, my emulator doesn't save anything in chatlogs_console, is your saving pvt chatlogs?
 

JynX

Posting Freak
Feb 6, 2016
710
438
Very nice command.
But, my emulator doesn't save anything in chatlogs_console, is your saving pvt chatlogs?
Are you running PlusEMU as this was coded on Sledmore's PlusEMU with that database structure. It is a simple fix for this, find the table that stores the private messages for the user and change the query to select it from that table. If you still need help let me know.
 
Last edited:

Hender

King Tinkerer
Mar 3, 2016
304
122
It's a nice command, although it's easier and efficient to have them in HK.

Sent from my GT-I9505 using Tapatalk
 

JynX

Posting Freak
Feb 6, 2016
710
438
It's a nice command, although it's easier and efficient to have them in HK.

Sent from my GT-I9505 using Tapatalk
Thanks Hender, the end-user could do it in any way they see fit. I thought that seeings as I was asked for this and even seen people using there Database to do it that it would be a bit more efficient to code a command where they can just view it in-game and not have to worry. :)
 

BigG

New Member
Oct 9, 2016
8
0
I'm running sledmore's plusemu, and I can't find anything with stored pm's :/
Are you running PlusEMU as this was coded on Sledmore's PlusEMU with that database structure. It is a simple fix for this, find the table that stores the private messages for the user and change the query to select it from that table. If you still need help let me know.
 

JynX

Posting Freak
Feb 6, 2016
710
438
I'm running sledmore's plusemu, and I can't find anything with stored pm's :/
Because JMG is a fucking retard and removed my original post, open a help thread and tag me in it so I can help you lol.
 

Seriosk

Programmer;
Oct 29, 2016
256
105
Firstly, take this as constructive criticism Jake, hopefully you can progress with these few tips :)

Using Utilities

Okay, I have a few things to point out here, lets call it constructive criticism. In this code below you shouldn't be coding methods that Plus has already implemented. Also your unix timestamp method shouldn't be placed inside a command file, its always good to place methods that may be used by multiple things in a Utilities file. You can learn what Utilities are

Lets pretend this situation happened. You had put that method in 20 different places because you needed it for 20 different things. What happens when you need to change that method? do you go back to all 20 and change them? Well with what you're doing you would have to. It's always good to have 1 method in 1 place that you only have to change once.

Naming Conventions
Your naming conventions are totally out of the standard C# naming conventions here, you don't have to follow them but it is known as good practice by other developers if you do follow them. At least you made both methods private which is good.

Private Methods
Private methods can be private static if they don't use any of the classes objects, you can read more about this this also goes for fields and other things, not just methods.

Caching the chatlogs
Why make your emulator go to all that work of getting 100 records when you could easily load them from a string list that you have previously populated with the chatlogs. This is known as caching, you can learn more about caching

Example:
Code:
List<string> _chatlogs;
(If plus doesn't already do this)

Plus Emulator also may even already have a method for this, I am not too sure.
On a plus note well done, you have come a long way. Good Tutorial.
Code:
private DateTime UnixTimeStampToDateTime(double unixTimeStamp)
{
    // Unix timestamp is seconds past epoch
    System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
    dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
    return dtDateTime;
}

private string getUsernameFromId(int userId)
{
    if (PlusEnvironment.GetHabboById(userId) != null)
        return PlusEnvironment.GetHabboById(userId).Username;

    using (IQueryAdapter db = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
    {
        db.SetQuery("SELECT `username` FROM `users` WHERE `id` = @id");
        db.AddParameter("id", userId);
        db.RunQuery();
        return db.getString();
    }
}
 
Last edited:

JynX

Posting Freak
Feb 6, 2016
710
438
Firstly, take this as constructive criticism Jake, hopefully you can progress with these few tips :)

Using Utilities

Okay, I have a few things to point out here, lets call it constructive criticism. In this code below you shouldn't be coding methods that Plus has already implemented. Also your unix timestamp method shouldn't be placed inside a command file, its always good to place methods that may be used by multiple things in a Utilities file. You can learn what Utilities are

Lets pretend this situation happened. You had put that method in 20 different places because you needed it for 20 different things. What happens when you need to change that method? do you go back to all 20 and change them? Well with what you're doing you would have to. It's always good to have 1 method in 1 place that you only have to change once.

Naming Conventions
Your naming conventions are totally out of the standard C# naming conventions here, you don't have to follow them but it is known as good practice by other developers if you do follow them. At least you made both methods private which is good.

Private Methods
Private methods can be private static if they don't use any of the classes objects, you can read more about this this also goes for fields and other things, not just methods.

Caching the chatlogs
Why make your emulator go to all that work of getting 100 records when you could easily load them from a string list that you have previously populated with the chatlogs. This is known as caching, you can learn more about caching

Example:
Code:
List<string> _chatlogs;
(If plus doesn't already do this)

Plus Emulator also may even already have a method for this, I am not too sure.
On a plus note well done, you have come a long way. Good Tutorial.
Code:
private DateTime UnixTimeStampToDateTime(double unixTimeStamp)
{
    // Unix timestamp is seconds past epoch
    System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
    dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
    return dtDateTime;
}

private string getUsernameFromId(int userId)
{
    if (PlusEnvironment.GetHabboById(userId) != null)
        return PlusEnvironment.GetHabboById(userId).Username;

    using (IQueryAdapter db = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
    {
        db.SetQuery("SELECT `username` FROM `users` WHERE `id` = @id");
        db.AddParameter("id", userId);
        db.RunQuery();
        return db.getString();
    }
}
Thanks for the feedback, I appreciate it greatly. I've been expanding my C# knowledge and will possibly clean this up at a later date and time :)
 

JayC

Well-Known Member
Aug 8, 2013
5,505
1,401
Someone asked for this, and I had no idea this was already released... So I did my own version of this not actually knowing, I won't steal your thunder but here is how I did it:

Code:
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using Plus.Communication.Packets.Outgoing.Notifications;
using Plus.HabboHotel.GameClients;

namespace Plus.HabboHotel.Rooms.Chat.Commands.Moderator
{
    class pmViewCommand : IChatCommand
    {
        public string PermissionRequired
        {
            get { return "command_viewpm"; }
        }

        public string Parameters
        {
            get { return "%From% %To%"; }
        }

        public string Description
        {
            get { return "Get the logs between 2 users"; }
        }

        public void Execute(GameClients.GameClient Session, Rooms.Room Room, string[] Params)
        {
            if (Params.Length == 1)
            {
                Session.SendWhisper("Please enter the username\'s of the user\'s you wish to see logs for.");
                return;
            }

            GameClient TargetClientOne = PlusEnvironment.GetGame().GetClientManager().GetClientByUsername(Params[1]);
            GameClient TargetClientTwo = PlusEnvironment.GetGame().GetClientManager().GetClientByUsername(Params[2]);

            if (TargetClientOne == null || TargetClientTwo == null)
            {
                Session.SendWhisper("An error occoured whilst finding that user, maybe they're not online.");
                return;
            }

            if (TargetClientOne.GetHabbo() == null || TargetClientTwo.GetHabbo() == null)
            {
                Session.SendWhisper("An error occoured whilst finding that user, maybe they're not online.");
                return;
            }

            if (TargetClientOne.GetHabbo().Username == Session.GetHabbo().Username || TargetClientTwo.GetHabbo().Username == Session.GetHabbo().Username)
            {
                Session.SendWhisper("You cannot log check yourself.");
                return;
            }

            string Message = CommandManager.MergeParams(Params, 2);

            StringBuilder List = new StringBuilder();
            List.Append("Viewing Last 15 Private Messages:\n=======================================================\n");
            foreach (List<string> pmMessage in TargetClientOne.GetHabbo().GetMessenger().GetMessagesTo(TargetClientTwo.GetHabbo().Id))
            {
                System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
                dtDateTime = dtDateTime.AddSeconds(Double.Parse(pmMessage[1])).ToLocalTime();
                List.Append(TargetClientOne.GetHabbo().Username + "--> " + TargetClientTwo.GetHabbo().Username + ": " + pmMessage[0] + " @" + dtDateTime + "\n\n");
            }
            Session.SendMessage(new MOTDNotificationComposer(List.ToString()));

        }
    }
}

And this goes under HabboMessenger.cs:
Code:
public List<List<string>> GetMessagesTo(int UserId)
        {
            List<List<string>> allMessages = new List<List<string>>();
            DataTable GetMessages = null;
            using (IQueryAdapter dbClient = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
            {
                dbClient.SetQuery("SELECT message,timestamp FROM `chatlogs_console` WHERE `to_id` = @id AND `from_id` = @from ORDER BY `timestamp` DESC LIMIT 15");
                dbClient.AddParameter("id", UserId);
                dbClient.AddParameter("from", this._userId);
                GetMessages = dbClient.getTable();

                if (GetMessages != null)
                {
                    foreach (DataRow Row in GetMessages.Rows)
                    {
                        List<string> toMessage = new List<string>();
                        toMessage.Add(Convert.ToString(Row["message"]));
                        toMessage.Add(Convert.ToString(Row["timestamp"]));
                        allMessages.Add(toMessage);
                    }

                  
                }
            }
            return allMessages;
        }
 
Firstly, take this as constructive criticism Jake, hopefully you can progress with these few tips :)

Using Utilities

Okay, I have a few things to point out here, lets call it constructive criticism. In this code below you shouldn't be coding methods that Plus has already implemented. Also your unix timestamp method shouldn't be placed inside a command file, its always good to place methods that may be used by multiple things in a Utilities file. You can learn what Utilities are

Lets pretend this situation happened. You had put that method in 20 different places because you needed it for 20 different things. What happens when you need to change that method? do you go back to all 20 and change them? Well with what you're doing you would have to. It's always good to have 1 method in 1 place that you only have to change once.

Naming Conventions
Your naming conventions are totally out of the standard C# naming conventions here, you don't have to follow them but it is known as good practice by other developers if you do follow them. At least you made both methods private which is good.

Private Methods
Private methods can be private static if they don't use any of the classes objects, you can read more about this this also goes for fields and other things, not just methods.

Caching the chatlogs
Why make your emulator go to all that work of getting 100 records when you could easily load them from a string list that you have previously populated with the chatlogs. This is known as caching, you can learn more about caching

Example:
Code:
List<string> _chatlogs;
(If plus doesn't already do this)

Plus Emulator also may even already have a method for this, I am not too sure.
On a plus note well done, you have come a long way. Good Tutorial.
Code:
private DateTime UnixTimeStampToDateTime(double unixTimeStamp)
{
    // Unix timestamp is seconds past epoch
    System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
    dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
    return dtDateTime;
}

private string getUsernameFromId(int userId)
{
    if (PlusEnvironment.GetHabboById(userId) != null)
        return PlusEnvironment.GetHabboById(userId).Username;

    using (IQueryAdapter db = PlusEnvironment.GetDatabaseManager().GetQueryReactor())
    {
        db.SetQuery("SELECT `username` FROM `users` WHERE `id` = @id");
        db.AddParameter("id", userId);
        db.RunQuery();
        return db.getString();
    }
}
Also, Great feedback. Only thing to point out is caching is only good if you're emulator never shuts off; otherwise all logs are lost :) Grabbing as many records as he did, I don't find helpful. I would limit it to something like I did (15)
 

JayC

Well-Known Member
Aug 8, 2013
5,505
1,401
If command_view_pms is rank 4 in the permission table it will be only for staff.

@JayCustom What's the difference from your code?
His method is sort of sloppy, and he calls to the database inside of the class (which is just a bad coding design). Mine utilizes the Messenger class which is inside of each habbo object, and uses that to get the pm's between 2 specific users.

As Hender suggests, I would rather see PM's inside of the CMS..
 

Users who are viewing this thread

Top