diff --git a/.gitignore b/.gitignore index b1a7903..b3dd952 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ __pycache__ +DiscordVerifier/out local.db diff --git a/DiscordVerifier/.DS_Store b/DiscordVerifier/.DS_Store new file mode 100755 index 0000000..ba90b33 Binary files /dev/null and b/DiscordVerifier/.DS_Store differ diff --git a/DiscordVerifier/DiscordVerifier.iml b/DiscordVerifier/DiscordVerifier.iml new file mode 100755 index 0000000..efa153e --- /dev/null +++ b/DiscordVerifier/DiscordVerifier.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DiscordVerifier/src/Main.java b/DiscordVerifier/src/Main.java new file mode 100755 index 0000000..e72bfef --- /dev/null +++ b/DiscordVerifier/src/Main.java @@ -0,0 +1,118 @@ +// 25216 --> uuid server +// 25224 --> send chat server + +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.UUID; + +public class Main extends JavaPlugin { + + Thread serversocket; + Thread uuidServerThread; + ServerSocket uuidServerSocket; + ServerSocket chatServerSocket; + + public void onEnable() { + getLogger().info("DiscordVerifier Plugin enabled"); + PluginManager pm = getServer().getPluginManager(); + + start_socketserver(); + start_uuid_server(); + + } + + public void start_uuid_server(){ + uuidServerThread = new Thread(() -> { + try{ + uuidServerSocket = new ServerSocket(25216); + while (true) { + Socket s = uuidServerSocket.accept(); + PrintWriter out = + new PrintWriter(s.getOutputStream(), true); + BufferedReader in = new BufferedReader( + new InputStreamReader(s.getInputStream())); + String inputLine; + inputLine = in.readLine(); + if (inputLine != null) { + System.out.println("minecraftname: " + inputLine); + Player player = getServer().getPlayer(inputLine); + if (player == null){ + out.println("PlayerError"); + }else { + System.out.println(player.getUniqueId().toString()); + UUID uuid = player.getUniqueId(); + out.println(uuid.toString()); + + } + } + } + + } catch (Exception e) { + System.out.println(e); + } + }); + + uuidServerThread.start(); + } + + + public void start_socketserver() { + serversocket = new Thread(() -> { + try{ + chatServerSocket = new ServerSocket(25224); + while (true) { + Socket s = chatServerSocket.accept(); + PrintWriter out = + new PrintWriter(s.getOutputStream(), true); + BufferedReader in = new BufferedReader( + new InputStreamReader(s.getInputStream())); + String inputLine; + inputLine = in.readLine(); + if (inputLine != null) { + + System.out.println("message: " + inputLine); + String[] info = inputLine.split("\t"); + String discordname = info[0]; + Player player = getServer().getPlayer(info[1]); + if (player == null){ + out.println("PlayerError"); + }else { + out.println("success"); + String code = info[2]; + send_chat(discordname, player, code); + } + } + } + + } catch (Exception e) { + System.out.println(e); + } + }); + + serversocket.start(); + } + + public void send_chat(String discordname, Player player, String code){ + player.sendMessage(discordname + " wants to link your account\nEnter this code in discord: " + code); + } + + public void onDisable() { + try { + chatServerSocket.close(); + uuidServerSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + uuidServerThread.stop(); + serversocket.stop(); + getLogger().info("Plugin disabled"); + } + + + +} diff --git a/DiscordVerifier/src/plugin.yml b/DiscordVerifier/src/plugin.yml new file mode 100755 index 0000000..7eb3e50 --- /dev/null +++ b/DiscordVerifier/src/plugin.yml @@ -0,0 +1,5 @@ +name: DiscordVerifier +main: Main +version: 0.0.1 +description: Verify Mincraftnames +api-version: 1.13 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/bot.py b/bot.py old mode 100644 new mode 100755 diff --git a/cogs/modcommands.py b/cogs/modcommands.py old mode 100644 new mode 100755 diff --git a/cogs/playerlink.py b/cogs/playerlink.py new file mode 100755 index 0000000..2927fae --- /dev/null +++ b/cogs/playerlink.py @@ -0,0 +1,181 @@ +import discord +from discord.ext import commands +import random +import string +import socket +import asyncio +import threading +from data import constants +from data.DatabaseConnection import * + +# 25216 --> uuid server +# 25224 --> send chat server + +def get_random_string(length): + # Random string with the combination of lower and upper case + letters = string.ascii_letters + "0123456789" + result_str = ''.join(random.choice(letters) for i in range(length)) + return result_str + +class PlayerError(Exception): + pass +class WrongCodeError(Exception): + pass + +def get_player_uuid(minecraftname): + HOST = '192.168.1.214' # The server's hostname or IP address + PORT = 25216 # The port used by the server + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + s.send(bytes("{}\n".format(minecraftname), encoding="ascii")) + data = s.recv(1024) + data_string = data.decode("utf-8").strip('\n') + if data_string == "PlayerError": + raise PlayerError() + return data_string + +def send_chat(discordname, minecraftname, code): + HOST = '192.168.1.214' # The server's hostname or IP address + PORT = 25224 # The port used by the server + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + s.send(bytes("{}\t{}\t{}\n".format(discordname, minecraftname, code), encoding="ascii")) + data = s.recv(1024) + data_string = data.decode("utf-8").strip('\n') + if data_string == "PlayerError": + raise PlayerError() + return data_string + +class PlayerLink(commands.Cog): + def __init__(self, client): + self.client = client + + def get_linked_role(self): + return discord.utils.get(self.client.get_guild(constants.WorldCraft).roles, id=constants.roleLinked) + + + @commands.command(name="Link") + async def link(self, ctx, arg): + + channelid = ctx.channel.id + authorid = ctx.author.id + code = get_random_string(8) + + + def check(message: discord.Message): + return message.channel.id == channelid and message.author.id == authorid + + # creates the embed for the link message + def create_embed(discord_author, minecraftname, timer, error=None, success=False): + embed = discord.Embed(title="WorldCraft Linker") + embed.add_field(name="How to:", value= "A code has been sent to your minecraft chat in the WorldCraft server.\nSend the code in this channel.") + embed.add_field(name="Minecraft Name:", value=f"{minecraftname}", inline=False) + embed.add_field(name="Discord Name:", value=f"{discord_author.mention}", inline=False) + + + # use dictionary and set color string before the message: example => r(ed):message + + if isinstance(error, WrongCodeError): + embed.add_field(name="Error", value=f"The code is wrong!", inline=False) + embed.colour = discord.Colour.red() + elif (success == True): + embed.add_field(name="Success", value=f"The link was successfull!", inline=False) + embed.colour = discord.Colour.green() + elif (timer.ended): + embed.add_field(name="Timer", value=f"The code is expired!", inline=False) + embed.colour = discord.Colour.red() + elif (timer.minutes == 2): + embed.add_field(name="Timer", value=f"The code will expire in { str(timer.minutes)} minutes", inline=False) + embed.colour = discord.Colour.orange() + elif (timer.minutes < 2): + embed.add_field(name="Timer", value=f"The code will expire in less than { str(timer.minutes + 1)} {'minutes' if timer.minutes + 1 > 1 else 'minute'}", inline=False) + embed.colour = discord.Colour.orange() + else: + embed.add_field(name="Timer", value=f"The code will expire in {str(timer.minutes).zfill(2)}:{str(timer.seconds).zfill(2)}", inline=False) + embed.colour = discord.Colour.orange() + return embed + + async def update_timer(timer, message): + while (not timer.ended): + await asyncio.sleep(1) + timer.update() + # maybe create a task from this because this takes some time -> timer not accurate + if ((timer.minutes < 2 and timer.seconds == 59) or (timer.minutes == 0 and timer.seconds == 0)): + asyncio.create_task(message.edit(embed=create_embed(ctx.author, arg, timer))) + + + try: + + uuid = get_player_uuid(arg) + send_chat(ctx.author.name, arg, code) + #message_send_time = + timer = Timer() + message = await ctx.send(embed=create_embed(ctx.author, arg, timer)) + + # Start timer in background + task = asyncio.create_task(update_timer(timer, message)) + + # Wait for the code response + msg = await self.client.wait_for('message', check=check) + + if msg.content == code: + + dbLinker = PlayerDBLinker() + try: + dbLinker.linkPlayer(uuid, authorid) + await ctx.author.add_roles(self.get_linked_role()) + timer.ended = True + await message.edit(embed=create_embed(ctx.author, arg, timer, success=True)) + except SQLInsertError: + await message.edit(embed=create_embed(ctx.author, arg, timer)) + finally: + dbLinker.closeconnections() + else: + # this stops the timer task + task.cancel() + timer.ended = True + await message.edit(embed=create_embed(ctx.author, arg, timer, WrongCodeError())) + + except PlayerError: + await ctx.send("Player '" + arg + "' not found") + #except: + # await ctx.send("Something went wrong") + + @commands.command(name="Unlink") + async def unlink(self, ctx, arg): + authorid = ctx.author.id + dbLinker = PlayerDBLinker() + try: + dbLinker.unlinkPlayer(arg, authorid) + await ctx.author.remove_roles(self.get_linked_role()) + await ctx.send("Unlinked your account") + except UnLinkError: + await ctx.send("The unlink was unsuccessfull") + except WrongMinecraftName: + await ctx.send("Wrong minecraft name!") + except PlayerNotLinked: + await ctx.send("Player not linked!") + finally: + dbLinker.closeconnections() + + +class Timer(): + def __init__(self): + self.minutes = 2 + self.seconds = 0 + self.ended = False + + def update(self): + if (self.minutes > 0 and self.seconds == 0): + self.minutes -= 1 + self.seconds = 59 + elif (self.seconds > 0): + self.seconds -= 1 + if (self.minutes == 0 and self.seconds == 0): + self.ended = True + + +def setup(client): + client.add_cog(PlayerLink(client)) diff --git a/cogs/servertatus.py b/cogs/servertatus.py new file mode 100755 index 0000000..82b32aa --- /dev/null +++ b/cogs/servertatus.py @@ -0,0 +1,46 @@ +import discord +from discord.ext import commands +from mcstatus import MinecraftServer + + +class ServerStatus(commands.Cog): + def __init__(self, client): + self.client = client + + @commands.command(name="Ping", aliases=["Status"]) + async def ping(self, ctx): + servers = {"Earth": "25568", "Moon": "25567"} + ip = "81.82.224.207:" + + # List of colours for the amount of servers that are online + upColours = [discord.Colour.red(), discord.Colour.orange(), discord.Colour.green()] + + embed = discord.Embed() + embed.set_author(name="Status") + + # Amount of servers that are online + upCounter = 2 + + # Add field for all the servers (for-loop so nothing has to be changed for future servers) + for server in servers: + minecraftServer = MinecraftServer.lookup(ip + servers[server]) + + try: + # Online + latency = f":green_circle: Server replied in {minecraftServer.ping()}ms." + except ConnectionRefusedError: + # Offline + upCounter -= 1 + latency = ":red_circle: Server is offline." + embed.add_field(name=server, value=latency, inline=False) + + # Set the embed colour + embed.colour = upColours[upCounter] + + # Bot's latency + embed.add_field(name="WorldCraft Bot", value=f":green_circle: {round(self.client.latency * 1000)}ms") + await ctx.send(embed=embed) + + +def setup(client): + client.add_cog(ServerStatus(client)) diff --git a/data/DatabaseConnection.py b/data/DatabaseConnection.py new file mode 100755 index 0000000..72ee79c --- /dev/null +++ b/data/DatabaseConnection.py @@ -0,0 +1,100 @@ +import mysql.connector + +class WrongMinecraftName(Exception): + pass +class SQLInsertError(Exception): + pass +class UnLinkError(Exception): + pass +class PlayerNotLinked(Exception): + pass + +class DatabaseConnection: + # databases -> worldcraft and worldcraft_discord + def __init__(self, database="worldcraft_discord"): + if database == "worldcraft_discord": + self.mydb = mysql.connector.connect( + host="192.168.1.251", + user="worldcraft_discord", + password="aquev5vcwhLwTdRt", + database=database + ) + elif database == "s13_worldcraft": + self.mydb = mysql.connector.connect( + host="192.168.1.251", + user="u13_f2zpWZpIKY", + password="@fZQ6Uu3+U^WH1i2JNemgTC7", + database=database + ) + + self.cursor = self.mydb.cursor() + + def get_db(self): + return self.mydb + + def get_cursor(self): + return self.cursor + + def close(self): + self.mydb.close() + +# this will save the uuid of the player and discord id +class PlayerDBLinker: + def __init__(self): + self.tablename = "playerlinks" + self.discorddbconn = DatabaseConnection(database="worldcraft_discord") + self.serverdbconn = DatabaseConnection(database="s13_worldcraft") + # check if table exists + cursor = self.discorddbconn.get_cursor() + cursor.execute("create table IF NOT EXISTS {} (id int NOT NULL PRIMARY KEY AUTO_INCREMENT, minecraftUUID varchar(36) NOT NULL, discordid TINYTEXT NOT NULL)".format(self.tablename)) + + def checkPlayerLink(self, minecraftUUID, discordid): + cursor = self.discorddbconn.get_cursor() + cursor.execute("select count(*) from {} where minecraftUUID='{}' AND discordid='{}'".format(self.tablename, minecraftUUID, discordid)) + res = cursor.fetchone() + return res[0] == 1 + + def minecraftUUIDused(self, minecraftUUID): + cursor = self.discorddbconn.get_cursor() + cursor.execute("select count(*) from {} where minecraftUUID='{}'".format(self.tablename, minecraftUUID)) + res = cursor.fetchone() + return res[0] >= 1 + + def discordidused(self, discordid): + cursor = self.discorddbconn.get_cursor() + cursor.execute("select count(*) from {} where discordid='{}'".format(self.tablename, discordid)) + res = cursor.fetchone() + return res[0] >= 1 + + def linkPlayer(self, minecraftUUID, discordid): + cursor = self.discorddbconn.get_cursor() + sql = "insert into {} (minecraftUUID, discordid) VALUES (%s, %s)".format(self.tablename) + val = (minecraftUUID, discordid) + + try: + cursor.execute(sql, val) + self.discorddbconn.get_db().commit() + except: + raise SQLInsertError() + + + def unlinkPlayer(self, minecraftname, discordid): + # get uuid from server database -> check if in linkerdatabase + serverdb_cursor = self.serverdbconn.get_cursor() + discorddb_cursor = self.discorddbconn.get_cursor() + sql = "SELECT playerUUID FROM playerprofiles WHERE playerName = '{}' LIMIT 1".format(minecraftname) + serverdb_cursor.execute(sql) + res = serverdb_cursor.fetchone() + if res == None: + raise WrongMinecraftName() + playeruuid = res[0] + if self.checkPlayerLink(playeruuid, discordid): + sql2 = f"DELETE FROM {self.tablename} WHERE minecraftUUID = '{playeruuid}' AND discordid = '{discordid}'" + discorddb_cursor.execute(sql2) + res2 = self.discorddbconn.get_db().commit() + else: + raise PlayerNotLinked() + + def closeconnections(self): + self.discorddbconn.close() + self.serverdbconn.close() diff --git a/data/constants.py b/data/constants.py old mode 100644 new mode 100755 index 5a7f752..f9a9d52 --- a/data/constants.py +++ b/data/constants.py @@ -9,6 +9,12 @@ roleAdmin = 730709756125249588 roleMod = 688328855605346339 roleOwner = 687996070986383436 +# User roles +roleLinked = 730709907275382904 # given when minecraft account is connected with discord account + +# Guild id +WorldCraft = 683422015394545665 + adminRoles = [roleAdmin, roleOwner] modPlusRoles = [roleAdmin, roleMod, roleOwner] diff --git a/files/images/server_icon.png b/files/images/server_icon.png old mode 100644 new mode 100755 diff --git a/functions/checks.py b/functions/checks.py old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt index 8532548..a0ea04f 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ discord.py==1.5.0 mcstatus==4.0.0 +mysql-connector-python==8.0.21 diff --git a/sockettest.py b/sockettest.py new file mode 100755 index 0000000..75448fa --- /dev/null +++ b/sockettest.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +import socket + +HOST = '127.0.0.1' # The server's hostname or IP address +PORT = 3333 # The port used by the server + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + s.sendall(b'Pieter suckt\n') + data = s.recv(1024) + print('Received', repr(data))