import os import discord import re import pprint import requests import platform from discord.ext import tasks, commands from discord.ext.commands import Bot reGame = re.compile('\s*([0-9.]+)\s+([0-9a-fA-F]+)\s+"(.*?)"' '\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+"(.*?)"\s+"(.*?)"' '\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s*',re.MULTILINE) reSummary = re.compile('^\s*(.*)\*\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+' '([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+MG\-on.*$',re.MULTILINE) games=[] recentlySaid=[] lastData = "" currentGames={} cnt=0 def parse_server_poll(data): global lastData if data != lastData: print(data) lastData = data m = reSummary.match(data) (listing, summary) = (m.groups()[0], m.groups()[1:]) m = reGame.findall(listing) r = {'games': [ { 'ip': a[0], 'port': int(a[1], 16), 'name': a[2], 'game_info': [ { 'tiredness': (x>>27) & ((1<<1)-1), 'preview': (x>>24) & ((1<<3)-1), 'control_mode': (x>>22) & ((1<<2)-1), 'unused': (x>>21) & ((1<<1)-1), 'game_per_set': (x>>18) & ((1<<3)-1), 'court': (x>>9) & ((1<<9)-1), 'skill_mode': (x>>7) & ((1<<2)-1), 'nb_set': (x>>5) & ((1<<2)-1), 'player_cfg': (x>>2) & ((1<<3)-1), 'trial': (x>>0) & ((1<<2)-1), } for x in [int(a[3], 16)]][0], 'max_ping': int(a[4], 16), 'elo': int(a[5], 16), 'nb_game': int(a[6], 16), 'tagline': a[7], 'score': a[8], 'other_elo': int(a[9], 16), 'give_up_rate': int(a[10], 16), 'reputation': int(a[11], 16), 'creation_time': int(a[12], 16), } for a in m], 'summary': summary} return(r) TOKEN = open("token.txt", "r").readline() intents = discord.Intents.default() intents.members = False intents.typing = False intents.presences = False intents.messages = True ch = None bot = Bot(command_prefix='!', intents=intents) @bot.event async def on_ready(): global ch print(f"Logged in as {bot.user.name}") print(f"Discord.py API version: {discord.__version__}") print(f"Python version: {platform.python_version()}") print(f"Running on: {platform.system()} {platform.release()} ({os.name})") print("-------------------") channels = [c for g in bot.guilds for c in g.text_channels] ch = discord.utils.get(channels, name="find-a-game") status_task.start() @tasks.loop(seconds=15.0) async def status_task(): global games, cnt, currentGames, recentlySaid data = requests.get("https://www.managames.com/tennis/online/TE4_ServerList.php?Poll=1") info = parse_server_poll(data.text.replace('\n',' ')) games = info['games'] out=[] nix=[] previewStrings = ('None', 'Service', 'Service + Rally', 'All + Danger Zone', 'All + Danger Zone + Aiming') skillStrings = ('Free', 'Fair', 'Realistic') controlStrings = ('Simulation', 'Arcade', 'Elite') for ongoing in currentGames.keys(): if ongoing not in [x['name'] for x in games]: out += [f"The {ongoing} game is finished. Final score: {currentGames[ongoing]['score']}"] nix += [ongoing] for xin in nix: del currentGames[xin] if len(games)>0 or len(out)>0: for game in games: s = "" if game['ip']=='0' and ' vs ' in game['name']: if game['name'] not in currentGames.keys(): players = game['name'].split(' vs ') s = f"{players[0]} started a game with {players[1]}." currentGames[game['name']] = game else: try: s = f"{game['name']} (elo: {game['elo']}, rep: {game['reputation']}%) wants to play a {game['game_info']['nb_set']}-set match. (Preview: {previewStrings[game['game_info']['preview']]}. Control: {controlStrings[game['game_info']['control_mode']]}. Skill: {skillStrings[game['game_info']['skill_mode']]}.)" except: pprint.pprint(game) s = "" if len(game['tagline'])>0: s += f" Comment: {game['tagline']}" if s != "" and s not in recentlySaid: out += [s] recentlySaid.append(s) if len(recentlySaid) > 5: recentlySaid = recentlySaid[-5:] for line in out: pprint.pprint(line) await ch.send(line) if len(games) == 0: recentlySaid = [] cnt+=1 bot.run(TOKEN) # 123.45.67.89 10E1 "Kate Kirby" B180B21 96 3B9 9 "test msg" "..." 0 16 32 61780D39 * 0 0 0 37 1E9C4 0 MG-on # * 0 0 0 37 1E9C4 0 MG-on # For each server, you'll see this : # Ip Port "Name" GameInfo MaxPing Elo NbGame "TagLine" "Score" OtherElo GiveUpRate Reputation CreationTimeInMs # Ip is 0 if the game is already started. # All numbers are hexadecimal. # GameInfo is a bitfield like this : # - Trial : 2 bits # - PlayerCfg : 3 ; 0 => singles ; 2 => Vs doubles ; 3 => Coop doubles (IIRC) # - NbSet : 2 # - SkillMode : 2 # - Court : 9 (will eventually be changed to 2 string, 1 for stadium & 1 for the surface) # - GamePerSet : 3 # - : 1 # - ControlMode : 2 # - Preview : 3 # - Tiredness : 1