MCCTeam / Minecraft-Console-Client

Lightweight console for Minecraft chat and automated scripts
https://mccteam.github.io
Other
1.67k stars 402 forks source link

Welcome message! #202

Closed ReOp14 closed 8 years ago

ReOp14 commented 8 years ago

So I had a cool idea to add to my bot but its difficult in concept. Is it possible to make it so when someone joins the game for The First Time it says "Player joined for the first time! Welcome!"?

Like when someone joins, it puts their name into a text file and if they havn't been logged into the text file before it would send the welcome message. Kinda like the Hangman game word file?

I understand if its impossible, but it would still be pretty cool!

ORelio commented 8 years ago

Hello,

You'd need to build a ChatBot.

As a variable it it you could use a HashSet to store usernames. The bot would need to load/save it on load/change.

Then on detection of "joined game" messages, test if the player was already seen. Of course, that does not handle UUID so changing names would trigger the welcome action again.

ReOp14 commented 8 years ago

Alright so I need to build a chat bot, im really still new to scripts, but this is different. May I have some help with Chat Bots? I can't seem to find any things about HashSet examples in your ChatBots.

Here is the Chat Syntax for when someone joins: Join> ReOp Join> (?!.*AntzBot)([a-zA-Z0-9_]+)$

It is not often that someone changes their name, so im not very worried about UUIDs. I also found something about grabbing from a file in HangmanGame.cs (string chooseword)

Also Mineplex moderators are telling me not to use bots, even though its not listed on the rules, so sorta mad.

ORelio commented 8 years ago

Don't know how you are writing your C# scripts but for developing ChatBots I'd recommend loading the whole Minecraft Console Client (Indev branch) project in Visual Studio (If you don't have it, Visual C# 2010 or greater is free) and head to ChatBot.cs.

Regarding HashSets, as other classes, when you do not know how to use it, I'd suggest declaring it eg by adding a variable eg private HashSet<string> seenPlayers = new HashSet<string>(); then in a method such as GetText() try using the hash set, eg type seenPlayers. and Visual Studio will suggests all the available methods as soon as you type the dot.

Alternatively you can read MSDN documentation: HashSet(T).

EDIT: Regarding moderation, I'd suggest to avoid making annoying/spammy bots. Even on my own server I usually refrain from using the Hangman bot. Although calculator is useful, that increases chat load. Maybe allow calculations by PM instead?

ReOp14 commented 8 years ago

Okay, this is quite a difficult task. I already have Visual Studio working. I see there is already TestBot.cs in the ChatBots folder and im using this as an example and a test. I am also not sure how to enable TestBot, but I tried putting

[TestBot]
enabled=true

in the INI file at the bottom but no luck with that.

Regarding that, I tried adding this using Visual Studio, a copy of TestBot but with a few changes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    public class TestJoin : ChatBot
    {
        HashSet<string> seenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            text = GetVerbatim(text);
            seenPlayers.GetHashCode;

            if (IsPrivateMessage(text, ref message, ref username))
            {
                ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
            }
            else if (IsChatMessage(text, ref message, ref username))
            {
                ConsoleIO.WriteLine("ReOp: " + username + " said : " + message);
            }
        }
    }
}

Obviously I can't test if this works if I can't enable TestBot, but I also don't know what/how to setup seenPlayers.. And how do I compile the code back into the "normal folder" with the exe, because you can't just put the code into a file ChatBot.cs because its not a script, but it is a ChatBot. (It would also say that you need to put "//MCCScript 1.0" at the top of the file.)

I really need some help making the ChatBot, although I am still really really new to this.

Pokechu22 commented 8 years ago

Try looking at sample-script-with-chatbot.cs, it gives an example.

So for instance, you could write a script like this to register a chatbot:

//MCCScript 1.0

MCC.LoadBot(new TestJoin());

//MCCScript Extensions

class TestJoin : ChatBot
{
    HashSet<string> seenPlayers = new HashSet<string>();
    public override void GetText(string text)
    {
        string message = "";
        string username = "";
        text = GetVerbatim(text);
        //seenPlayers.GetHashCode;

        if (IsPrivateMessage(text, ref message, ref username))
        {
            ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
        }
        else if (IsChatMessage(text, ref message, ref username))
        {
            ConsoleIO.WriteLine("ReOp: " + username + " said : " + message);
        }
    }
}
ORelio commented 8 years ago

Please fully read comments in ChatBot.cs, it explains how to load and debug your bot. Once finished, you can export it in a script as @Pokechu22 says above.

ReOp14 commented 8 years ago

Hey im having a problem with the chat bot I have been making.

//MCCScript 1.0

MCC.LoadBot(new TestJoin());

//MCCScript Extensions

class TestJoin : ChatBot
{
    private HashSet<string> seenPlayers = new HashSet<string>();
    public override void GetText(string text)
    {
        string message = "";
        string username = "";
        text = GetVerbatim(text);

        if (message == Join> )
        {
            SendText{"Welcome to the MPS for the first time" + seenPlayers + "!"};
        }
    }
}

It doesn't work at all and just gives bracket error. But when it does work, it doesn't detect when someone joins and I have no idea how HashSet even works. I did notice that there was LoadDistinctEntriesFromFile in ChatBot.cs. Can you please fix the whole script? I have been trying to find a solution for 3 hours with no luck.

ORelio commented 8 years ago

Please use Visual Studio. It underlines your code for syntax errors and facilitates debugging. So do NOT embed your ChatBot into a script file until it is finished and ready to use.

Quote from ChatBot.cs (Indev branch, not master):

Welcome to the Bot API file ! The virtual class "ChatBot" contains anything you need for creating chat bots Inherit from this class while adding your bot class to the "ChatBots" folder. Override the methods you want for handling events: Initialize, Update, GetText.

For testing your bot you can add it in McTcpClient.cs (see comment at line ~119). Your bot will be loaded everytime MCC is started so that you can test/debug.

Once your bot is fully written and tested, you can export it a standalone script. This way it can be loaded in newer MCC builds, without modifying MCC itself. See config/sample-script-with-chatbot.cs for a ChatBot script example.

In McTcpClient.cs:

if (Settings.AutoRespond_Enabled) { BotLoad(/* ... */); }
//Add your ChatBot here by uncommenting and adapting
//BotLoad(new ChatBots.YourBot());

Then you can compile and debug your bot from Visual Studio, that is MUCH EASIER. The bot gets loaded automatically upon joining (for testing purposes).

This line:

if (message == Join> )

Contains two mistakes:

if (message.StartsWith("Join>"))

The seenplayers hashset must be loaded on bot load from a file, the LoadDistinctEntriesFromFile method is indeed suited for that. For writing you can convert to a string array and write everything:

File.WriteAllLines("seenplayers.txt", seenplayers.ToArray());
ReOp14 commented 8 years ago

I can't get the script to do anything when I test it.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class TestJoin : ChatBot
    {
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            text = GetVerbatim(text);
            if (message.StartsWith("Join>"))
            {
                LogToConsole("test");
                File.WriteAllLines("seenplayers.txt", SeenPlayers.ToArray());
                SendText("Welcome to the server for the first time " + username);
            }
        }
    }
}

There is no errors or anything (in visual studio), its just I'm not sure how to make the script work correctly. I tried joining the game a few times, Join> ReOp and the LogToConsole does not log "test". When I do put LogToConsole without the If statement, it will log "test".

I also tried to see whether the File.WriteAllLines("seenplayers.txt", SeenPlayers.ToArray()); worked without If, I put it under the GetVerbatim line and it did nothing. I also manually created a seenplayers.txt for it also.

I don't have a lot of time today because im heading over to Anaheim for MineCon this weekend, so apologizes if I missed something obvious.

ORelio commented 8 years ago

Are you sure you added BotLoad(new ChatBots.TestJoin()); at the right place in McTcpClient.cs ? Also don't forget to put the newly seen username in the hashset with SeenPlayers.Add(/* the name */); And load the hashset on bot load, of course ;)

ReOp14 commented 8 years ago

Alright, back from Minecon. I put the code and put it in its own file (Join.cs) under ChatBots folder in Visual Studio.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class Join : ChatBot
    {
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            text = GetVerbatim(text);
            File.WriteAllLines("seenplayers.txt", SeenPlayers.ToArray());
            if (message.StartsWith("Join>"))
            {
                LogToConsole("test");
                SeenPlayers.Add(username);
                File.WriteAllLines("seenplayers.txt", SeenPlayers.ToArray());
                SendText("Welcome to the server for the first time " + username);
            }
        }
    }
}

And I also put if (Settings.AutoRespond_Enabled) { BotLoad(new ChatBots.Join()); } in McTcpClient.cs (in the correct location) (before I tried just /script on startup) and I compiled using "f5" and found the exe file and used it, but I have not gotten it to work at all and no text of usernames in seenplayers.txt Not sure what is going on. I think the script does not start perhaps?

ORelio commented 8 years ago

As you put in your condition, the bot will load only if AutoRespond is enabled. The 'if' part is not needed, just leave the BotLoad() instruction alone, not wrapped in a condition ^^

... Was Minecon great this year? :)

ReOp14 commented 8 years ago

I removed the condition into: BotLoad(new ChatBots.Join()); there was no errors in visual studio. I tested a few times at after joining Join> ReOp it would not log my name in SeenPlayers.txt nor respond in anyway at all.

Minecon was pretty good, a ton of little kids but that's alright, there was a band and lots of work done at Minecon this year. I have honestly never watched any of the other Minecons on the live stream so I can't compare it.

ORelio commented 8 years ago

Just print everything you get from the server, that's the easiest way to check you are receiving messages:

ConsoleIO.WriteLine("GetText: " + text);

Insert the above as first line in your GetText() method. Alternatively you can use debugging by putting a breakpoint on GetText() (click on the left column on the right line while editing code) but when the breakpoint gets hit, your MCC instance will pause, and the server times out and closes connection if you stay in that paused state for more than 30 seconds. So use either debug prints or Visual Studio debugging depending on what you need ;)

Regarding Minecon, I never went to a Minecon nor watched stream but, well, thanks for your feedback :smile:

ReOp14 commented 8 years ago

I think the problem is that the if (message.StartsWith("Join> ")) is not detecting the message, (I also used this without a space).

I did that GetText thing you said, it repeated everything said in chat. GetText: Join> ReOp Also, I am not sure how breakpoints work nor how they can help me so I didn't try that. I also tried using different accounts to see if it was my player name.

ORelio commented 8 years ago
string message = "";
string username = "";
text = GetVerbatim(text);
File.WriteAllLines("seenplayers.txt", SeenPlayers.ToArray());
if (message.StartsWith("Join>"))

The script is never assigning the message variable so it stays empty. Maybe you were intending either if (text.StartsWith or message = GetVerbatim(text)?

ReOp14 commented 8 years ago

Using this bot code I found a few things:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class Join : ChatBot
    {
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            message = GetVerbatim(text);
            File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
            if (message.StartsWith("Join> "))
            {
                ConsoleIO.WriteLine("GetText: " + text);
                ConsoleIO.WriteLine("UserName: " + username);
                SeenPlayers.Add(username);
                SeenPlayers.Add(text);
                File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
                SendText("Welcome to the Server for the first time " + username + "!");
            }
        }
    }
}

It does detect with "Join> ", Thanks easy fix. Although, while testing it says: Welcome to the Server for the first time ! in Minecraft chat.

Although in the GetText, there is a problem. (Weird characters) GetText: 88Join> r77ReOpr Also this is logged into the Seen Players with the same weird characters.

About the SeenPlayers.Add(username); it says no text at all and makes a blank line in the SeenPlayers.txt file.

I am also still not allowed to use "bots" on Mineplex, not because it spams, it just they don't want bots. Although not posted on the rules, they still come on the server and tell me not to use them. I tried talking to the Mineplex staff team at Minecon, but they also said no.

ORelio commented 8 years ago

username is never assigned, that's why it stays empty and empty lines are added to your file. Maybe you'd like to perform username = message.Substring(6); to extract username from the message. Weird characters are §c color codes. These can be stripped with GetVerbatim() already in your code. Regarding bots on the server you are playing on, I can't really help on that side, sorry :sweat_smile:

ReOp14 commented 8 years ago

Hey, got that to work luckily. I still need to make sure it only does it once still. I tried using LoadDistinctEntriesFromFile but I couldn't find how to use it correctly.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class Join : ChatBot
    {
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            message = GetVerbatim(text);
            File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
            if (message.StartsWith("Join> "))
            {
                username = message.Substring(6);
                text = GetVerbatim(text);
                ConsoleIO.WriteLine("UserName: " + username);
                SeenPlayers.Add(username);
                File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
                SendText("Welcome to the Server for the first time " + username + "!");
            }
        }
    }
}

How can I make it only welcome the player if they only joined for the first time. How to grab and check the string from SeenPlayers.txt?

ORelio commented 8 years ago
public Join()
{
    for (string name in LoadDistinctEntriesFromFile("SeenPlayers.txt"))
        SeenPlayers.Add(name);
}
ReOp14 commented 8 years ago

Got pretty far in concept but It keeps bypassing my if statement. I am not sure how to make the code colored but It would help a lot I think if I shared an image also. (And im getting that weird warning, not sure why that is) visual studio or

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class Join : ChatBot
    {
        private string name;
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            message = GetVerbatim(text);
            foreach (string name in LoadDistinctEntriesFromFile("SeenPlayers.txt")) //Checks to see if username is in SeenPlayers.txt
            {
                SeenPlayers.Add(name);
            }
            if (message.StartsWith("Join> ")) //If message is a player joining the server
            {
                username = message.Substring(6);
                text = GetVerbatim(text);
                ConsoleIO.WriteLine("Username: " + username);
                if (username == name) //Checks to see if username is in SeenPlayers.txt
                {
                    ConsoleIO.WriteLine("Join: The username " + username + " has already joined before.");
                }
                else //If username is not in SeenPlayers.txt
                {
                    ConsoleIO.WriteLine("Join: " + username + " has been logged into SeenPlayers.txt");
                    SeenPlayers.Add(username);
                    File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
                    SendText("Welcome to the Server for the first time " + username + "!");
                }
            }
        }
    }
}

The problem is that the if statement is NOT true so it goes to the else below it. I also tried replacing else with if (username != name) but I received the same result. In the SeenPlayers.txt file it does list the name inside it.

AntzBot
ReOp
DeOp
ORelio commented 8 years ago

To post colored code on GitHub:

// ````C# <- Or any language name here
// Your code here
// ````

Of course remove the // on your case, I'm putting them because otherwise GitHub would interpret them. Regarding your code: 1) Loading the file should happen only once, in the constructor. Just move it in a separate method called Join(), see my message above. 2) What is this unused "name" variable? Just remove it and and use SeenPlayers:

if (SeenPlayers.Contains(username))
{

instead of if (username == name) (name is always null so obviously that's always false.)

ReOp14 commented 8 years ago

Okay this is very strange. Got it working until I tried restarting it and it forgot everything. Ill try and explain but its hard enough to even think about it. Alright so it welcomes me to the MPS for the first time and I try rejoining and it says:

16:36:56 Join> ReOp
Username: ReOp
Join: The username ReOp has already joined before.

So that's what I was aiming for, but when I restarted AntzBot, it welcomes itself for the first time even though the username is already in the SeenPlayers.txt, but it also deletes ReOp from the bottom of the file but I also noticed lowercase of the usernames which I did not put there. (When I join on ReOp it also says Welcome for the first time)

antzbot
reop
AntzBot
ReOp

My code for Join.cs: (sorry for the 46 lines of spam) (yay colors) EDIT: Visual studio gives an error when I use for, so I use foreach

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MinecraftClient.ChatBots
{
    /// <summary>
    /// Example of message receiving.
    /// </summary>

    class Join : ChatBot
    {
        private string name;
        private HashSet<string> SeenPlayers = new HashSet<string>();
        public Join()
        {
            foreach (string username in LoadDistinctEntriesFromFile("SeenPlayers.txt"))
                SeenPlayers.Add(username);
        }
        public override void GetText(string text)
        {
            string message = "";
            string username = "";
            message = GetVerbatim(text);
            if (message.StartsWith("Join> ")) //If message is a player joining the server
            {
                username = message.Substring(6);
                text = GetVerbatim(text);
                ConsoleIO.WriteLine("Username: " + username);
                if (SeenPlayers.Contains(username))
                {
                    ConsoleIO.WriteLine("Join: The username " + username + " has already joined before.");
                }
                else //If username is not in SeenPlayers.txt
                {
                    ConsoleIO.WriteLine("Join: " + username + " has been logged into SeenPlayers.txt");
                    SeenPlayers.Add(username);
                    File.WriteAllLines("SeenPlayers.txt", SeenPlayers.ToArray());
                    SendText("Welcome to the Server for the first time " + username + "!");
                }
            }
        }
    }
}
ORelio commented 8 years ago

Oh, that's because LoadDistinctEntries will provide lowercase usernames: Source. You can use username = message.Substring(6).ToLower(); to use lowercase usernames too. This way everything is lowercase and names are properly saved and compared.

ReOp14 commented 8 years ago

Thanks got it all to work! Can't use it because of Mineplex's rules, but I'm having tons of enjoyment making these. Your .ToLower() fixed it, I also had to add things so the message printed in chat was also not lowercase. All is well.