class MinecraftBot(): def __init__(self, username, server, port, commands): self.username = username self.server = server self.port = port self.commands = commands self.bot = Connection(server, port, username=username, allowed_versions=[47]) self.bot.register_packet_listener(self.handle_join_game, clientbound.play.JoinGamePacket) log("INFO", "Trying to connect {0} to {1}:{2}.".format(username, server, port)) self.bot.connect() threading.Thread(target=self.execute_go, args=["/go"], daemon=True).start() def execute_go(self, command): time.sleep(15) self.execute_command(command) def handle_join_game(self, join_game_packet): log("INFO", "{0} is connected to {1}:{2}.".format(self.username, self.server, self.port)) time.sleep(3) self.execute_command(self.commands[0]) def execute_command(self, command): log("INFO", "{0} is doing command {1}".format(self.username, command)) packet = serverbound.play.ChatPacket() packet.message = command self.bot.write_packet(packet) def disconnect(self): log("INFO", "Disconnecting {0} from {1}:{2}.".format(self.username, self.server, self.port)) self.bot.disconnect() log("INFO", "{0} is disconnected of {1}:{2}.".format(self.username, self.server, self.port))
def main(): authenticate(MINECRAFT_USERNAME, MINECRAFT_PASSWORD) connection = Connection(SERVER_IP, 25565, auth_token=auth_token) connection.register_packet_listener(packet_handler.handle_join_game, packets.JoinGamePacket) # Lambda function is used to inject the connection so the handle_chat function can also send chat packets. # That's important for AFK connection.register_packet_listener( lambda chat_packet: packet_handler.handle_chat( chat_packet, connection), packets.ChatMessagePacket) connection.register_packet_listener(packet_handler.handle_player_list, packets.PlayerListItemPacket) connection.connect() # Allows user to enter chat messages in the terminal and we'll send them. # Sometimes needed to run /l bedwars. while True: try: text = input() packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Exiting bedwars bot.") sys.exit()
def main(): options = get_options() auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as " + auth_token.username) connection = Connection(options.address, options.port, auth_token) connection.connect() def print_chat(chat_packet): print("Position: " + str(chat_packet.position)) print("Data: " + chat_packet.json_data) connection.register_packet_listener(print_chat, ChatMessagePacket) while True: try: text = input() packet = ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
class Main(object): def __init__(self, options): self.auth_token = authentication.AuthenticationToken() try: self.auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as " + self.auth_token.username) self.network = Connection(options.address, options.port, self.auth_token) self.network.connect() self.register_listeners() #sys.stdout = Speaker(self) while not self.network.playing: pass self.respawn() while True: try: self.tick() except KeyboardInterrupt: print("Bye!") sys.exit() def register_listeners(self): self.network.register_packet_listener(self.print_chat, ChatMessagePacket) self.network.register_packet_listener(self.set_pos, PlayerPositionAndLookPacket) self.network.register_packet_listener(self.recieve_plugin_message, PluginMessage) self.network.register_packet_listener(self.set_health, UpdateHealth) def tick(self): text = input() self.speak(text) def speak(self, message): packet = ChatPacket() packet.message = message self.network.write_packet(packet) def set_health(self, health_packet): if health_packet.health == 0.0: print "RESPAWN" self.respawn() def respawn(self): packet = ClientStatus() packet.action_id = 0 self.network.write_packet(packet) #print packet def print_chat(self, chat_packet): print type(chat_packet) print("Position: " + str(chat_packet.position)) print("Data: " + chat_packet.json_data) def recieve_plugin_message(self, plugin_message): data = plugin_message.data.split("|") if len(data) == 2 and data[0] == "MC": if data[1] == "Brand": print "Brand:", plugin_message.channel else: print "PLUGIN MESSAGE:", data[1], plugin_message.channel
def main(): options = get_options() if options.offline: print("Connecting in offline mode") connection = Connection( options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as " + auth_token.username) connection = Connection( options.address, options.port, auth_token=auth_token) connection.connect() def print_chat(chat_packet): print("Position: " + str(chat_packet.position)) print("Data: " + chat_packet.json_data) connection.register_packet_listener(print_chat, ChatMessagePacket) while True: try: text = input() if text == "/respawn": print("respawning...") packet = ClientStatusPacket() packet.action_id = ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
class MinecraftDiscordBridge(): def __init__(self): self.return_code = os.EX_OK self.session_token = "" self.uuid_cache = bidict() self.webhooks = [] self.bot_username = "" self.next_message_time = datetime.now(timezone.utc) self.previous_message = "" self.player_list = bidict() self.previous_player_list = bidict() self.accept_join_events = False self.tab_header = "" self.tab_footer = "" # Initialize the discord part self.discord_bot = discord.Client() self.config = Configuration("config.json") self.connection_retries = 0 self.auth_token = None self.connection = None self.setup_logging(self.config.logging_level) self.database_session = DatabaseSession() self.logger = logging.getLogger("bridge") self.database_session.initialize(self.config) self.bot_perms = discord.Permissions() self.bot_perms.update(manage_messages=True, manage_webhooks=True) # Async http request pool self.req_future_session = FuturesSession(max_workers=100) self.reactor_thread = Thread(target=self.run_auth_server, args=(self.config.auth_port, )) self.aioloop = asyncio.get_event_loop() # We need to import twisted after setting up the logger because twisted hijacks our logging from . import auth_server auth_server.DATABASE_SESSION = self.database_session if self.config.es_enabled: if self.config.es_auth: self.es_logger = ElasticsearchLogger(self.req_future_session, self.config.es_url, self.config.es_username, self.config.es_password) else: self.es_logger = ElasticsearchLogger(self.req_future_session, self.config.es_url) @self.discord_bot.event async def on_ready(): # pylint: disable=W0612 self.logger.info("Discord bot logged in as %s (%s)", self.discord_bot.user.name, self.discord_bot.user.id) self.logger.info( "Discord bot invite link: %s", discord.utils.oauth_url(client_id=self.discord_bot.user.id, permissions=self.bot_perms)) await self.discord_bot.change_presence( activity=discord.Game("mc!help for help")) self.webhooks = [] session = self.database_session.get_session() channels = session.query(DiscordChannel).all() session.close() for channel in channels: channel_id = channel.channel_id discord_channel = self.discord_bot.get_channel(channel_id) if discord_channel is None: session = self.database_session.get_session() session.query(DiscordChannel).filter_by( channel_id=channel_id).delete() session.close() continue channel_webhooks = await discord_channel.webhooks() found = False for webhook in channel_webhooks: if webhook.name == "_minecraft" and webhook.user == self.discord_bot.user: self.webhooks.append(webhook.url) found = True self.logger.debug("Found webhook %s in channel %s", webhook.name, discord_channel.name) if not found: # Create the hook await discord_channel.create_webhook(name="_minecraft") @self.discord_bot.event async def on_message(message): # pylint: disable=W0612 # We do not want the bot to reply to itself if message.author == self.discord_bot.user: return this_channel = message.channel.id # PM Commands if message.content.startswith("mc!help"): try: send_channel = message.channel if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = self.get_discord_help_string() await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return elif message.content.startswith("mc!register"): try: send_channel = message.channel if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel session = self.database_session.get_session() discord_account = session.query(DiscordAccount).filter_by( discord_id=message.author.id).first() if not discord_account: new_discord_account = DiscordAccount(message.author.id) session.add(new_discord_account) session.commit() discord_account = session.query( DiscordAccount).filter_by( discord_id=message.author.id).first() new_token = self.generate_random_auth_token(16) account_link_token = AccountLinkToken( message.author.id, new_token) discord_account.link_token = account_link_token session.add(account_link_token) session.commit() msg = "Please connect your minecraft account to `{}.{}:{}` in order to link it to this bridge!"\ .format(new_token, self.config.auth_dns, self.config.auth_port) session.close() del session await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return # Global Commands elif message.content.startswith("mc!chathere"): if isinstance(message.channel, discord.abc.PrivateChannel): msg = "Sorry, this command is only available in public channels." await message.channel.send(msg) return if message.author.id not in self.config.admin_users: await message.delete() try: dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() dm_channel = message.author.dm_channel msg = "Sorry, you do not have permission to execute that command!" await dm_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return session = self.database_session.get_session() channels = session.query(DiscordChannel).filter_by( channel_id=this_channel).all() if not channels: new_channel = DiscordChannel(this_channel) session.add(new_channel) session.commit() session.close() del session webhook = await message.channel.create_webhook( name="_minecraft") self.webhooks.append(webhook.url) msg = "The bot will now start chatting here! To stop this, run `mc!stopchathere`." await message.channel.send(msg) else: msg = "The bot is already chatting in this channel! To stop this, run `mc!stopchathere`." await message.channel.send(msg) return elif message.content.startswith("mc!stopchathere"): if isinstance(message.channel, discord.abc.PrivateChannel): msg = "Sorry, this command is only available in public channels." await message.channel.send(msg) return if message.author.id not in self.config.admin_users: await message.delete() try: dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() dm_channel = message.author.dm_channel msg = "Sorry, you do not have permission to execute that command!" await dm_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return session = self.database_session.get_session() deleted = session.query(DiscordChannel).filter_by( channel_id=this_channel).delete() session.commit() session.close() for webhook in await message.channel.webhooks(): if webhook.name == "_minecraft" and webhook.user == self.discord_bot.user: # Copy the list to avoid some problems since # we're deleting indicies form it as we loop # through it if webhook.url in self.webhooks[:]: self.webhooks.remove(webhook.url) await webhook.delete() if deleted < 1: msg = "The bot was not chatting here!" await message.channel.send(msg) return else: msg = "The bot will no longer chat here!" await message.channel.send(msg) return elif message.content.startswith("mc!tab"): send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel player_list = ", ".join( list(map(lambda x: x[1], self.player_list.items()))) msg = "{}\n" \ "Players online: {}\n" \ "{}".format(self.escape_markdown( self.strip_colour(self.tab_header)), self.escape_markdown( self.strip_colour(player_list)), self.escape_markdown( self.strip_colour(self.tab_footer))) await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return elif message.content.startswith("mc!botlink"): send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = "Use the following link to invite this bot to a guild:\n{}".format( discord.utils.oauth_url( client_id=self.discord_bot.user.id, permissions=self.bot_perms)) await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return elif message.content.startswith("mc!about"): send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = "This bot is running minecraft-discord-bridge version {}.\n" \ "The source code is available at https://github.com/starcraft66/minecraft-discord-bridge" \ .format(minecraft_discord_bridge.__version__) await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return elif message.content.startswith("mc!"): # Catch-all send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): await message.delete() dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = "Unknown command, type `mc!help` for a list of commands." await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return elif "https://discord.gg" in message.content.lower(): await message.delete() # Deletes the message # Add something more if you want to msg = f"{message.author.mention} invites aren't allowed!" # Your message await send_channel.send(msg) elif not message.author.bot: session = self.database_session.get_session() channel_should_chat = session.query(DiscordChannel).filter_by( channel_id=this_channel).first() if channel_should_chat: await message.delete() discord_user = session.query(DiscordAccount).filter_by( discord_id=message.author.id).first() if discord_user: if discord_user.minecraft_account: minecraft_uuid = discord_user.minecraft_account.minecraft_uuid session.close() del session minecraft_username = self.mc_uuid_to_username( minecraft_uuid) # Max chat message length: 256, bot username does not count towards this # Does not count|Counts # <BOT_USERNAME> minecraft_username: message padding = 2 + len(minecraft_username) message_to_send = self.remove_emoji( message.clean_content.encode('utf-8').decode( 'ascii', 'replace')).strip() message_to_discord = self.escape_markdown( message.clean_content) total_len = padding + len(message_to_send) if total_len > 256: message_to_send = message_to_send[:(256 - padding)] message_to_discord = message_to_discord[:( 256 - padding)] elif not message_to_send: return session = self.database_session.get_session() channels = session.query(DiscordChannel).all() session.close() del session if message_to_send == self.previous_message or \ datetime.now(timezone.utc) < self.next_message_time: send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = "Your message \"{}\" has been rate-limited.".format( message.clean_content) await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send( msg) await asyncio.sleep(3) await error_msg.delete() return self.previous_message = message_to_send self.next_message_time = datetime.now( timezone.utc) + timedelta( seconds=self.config.message_delay) self.logger.info( "Outgoing message from discord: Username: %s Message: %s", minecraft_username, message_to_send) for channel in channels: discord_channel = self.discord_bot.get_channel( channel.channel_id) if not discord_channel: session = self.database_session.get_session( ) session.query(DiscordChannel).filter_by( channel_id=channel.channel_id).delete( ) session.close() continue webhooks = await discord_channel.webhooks() for webhook in webhooks: if webhook.name == "_minecraft": await webhook.send( username=minecraft_username, avatar_url= "https://visage.surgeplay.com/face/160/{}" .format(minecraft_uuid), content=message_to_discord) packet = serverbound.play.ChatPacket() packet.message = "{}: {}".format( minecraft_username, message_to_send) self.connection.write_packet(packet) else: send_channel = message.channel try: if isinstance(message.channel, discord.abc.GuildChannel): dm_channel = message.author.dm_channel if not dm_channel: await message.author.create_dm() send_channel = message.author.dm_channel msg = "Unable to send chat message: there is no Minecraft account linked to this discord " \ "account, please run `mc!register`." await send_channel.send(msg) return except discord.errors.Forbidden: if isinstance(message.author, discord.abc.User): msg = "{}, please allow private messages from this bot.".format( message.author.mention) error_msg = await message.channel.send(msg) await asyncio.sleep(3) await error_msg.delete() return finally: session.close() del session else: session.close() del session def run(self): self.logger.debug( "Checking if the server {} is online before connecting.") if not self.config.mc_online: self.logger.info("Connecting in offline mode...") while not self.is_server_online(): self.logger.info( 'Not connecting to server because it appears to be offline.' ) time.sleep(15) self.bot_username = self.config.mc_username self.connection = Connection( self.config.mc_server, self.config.mc_port, username=self.config.mc_username, handle_exception=self.minecraft_handle_exception) else: self.auth_token = authentication.AuthenticationToken() try: self.auth_token.authenticate(self.config.mc_username, self.config.mc_password) except YggdrasilError as ex: self.logger.info(ex) sys.exit(os.EX_TEMPFAIL) self.bot_username = self.auth_token.profile.name self.logger.info("Logged in as %s...", self.auth_token.profile.name) while not self.is_server_online(): self.logger.info( 'Not connecting to server because it appears to be offline.' ) time.sleep(15) self.connection = Connection( self.config.mc_server, self.config.mc_port, auth_token=self.auth_token, handle_exception=self.minecraft_handle_exception) self.register_handlers(self.connection) self.connection_retries += 1 self.reactor_thread.start() self.connection.connect() try: self.aioloop.run_until_complete( self.discord_bot.start(self.config.discord_token)) except (KeyboardInterrupt, SystemExit): # log out of discord self.aioloop.run_until_complete(self.discord_bot.logout()) # log out of minecraft self.connection.disconnect() # shut down auth server from twisted.internet import reactor reactor.callFromThread(reactor.stop) # clean up auth server thread self.reactor_thread.join() finally: # close the asyncio event loop discord uses self.aioloop.close() return self.return_code def mc_uuid_to_username(self, mc_uuid: str): if mc_uuid not in self.uuid_cache: try: short_uuid = mc_uuid.replace("-", "") mojang_response = self.req_future_session.get( "https://api.mojang.com/user/profiles/{}/names".format( short_uuid)).result().json() if len(mojang_response) > 1: # Multiple name changes player_username = mojang_response[-1]["name"] else: # Only one name player_username = mojang_response[0]["name"] self.uuid_cache[mc_uuid] = player_username return player_username except RequestException as ex: self.logger.error(ex, exc_info=True) self.logger.error( "Failed to lookup %s's username using the Mojang API.", mc_uuid) else: return self.uuid_cache[mc_uuid] def mc_username_to_uuid(self, username: str): if username not in self.uuid_cache.inv: try: player_uuid = self.req_future_session.get( "https://api.mojang.com/users/profiles/minecraft/{}". format(username)).result().json()["id"] long_uuid = uuid.UUID(player_uuid) self.uuid_cache.inv[username] = str(long_uuid) return player_uuid except RequestException: self.logger.error( "Failed to lookup %s's username using the Mojang API.", username) else: return self.uuid_cache.inv[username] def get_discord_help_string(self): help_str = ( "Admin commands:\n" "`mc!chathere`: Starts outputting server messages in this channel\n" "`mc!stopchathere`: Stops outputting server messages in this channel\n" "User commands:\n" "`mc!tab`: Sends you the content of the server's player/tab list\n" "`mc!register`: Starts the minecraft account registration process\n" "`mc!botlink`: Sends you the link to invite this bot to a guild\n" "`mc!about`: Sends you information about the running bridge\n" "To start chatting on the minecraft server, please register your account using `mc!register`." ) return help_str # https://stackoverflow.com/questions/33404752/removing-emojis-from-a-string-in-python def remove_emoji(self, dirty_string): emoji_pattern = re.compile( "[" u"\U0001F600-\U0001F64F" # emoticons u"\U0001F300-\U0001F5FF" # symbols & pictographs u"\U0001F680-\U0001F6FF" # transport & map symbols u"\U0001F1E0-\U0001F1FF" # flags (iOS) u"\U0001F900-\U0001FAFF" # CJK Compatibility Ideographs # u"\U00002702-\U000027B0" # u"\U000024C2-\U0001F251" "]+", flags=re.UNICODE) return emoji_pattern.sub(r'', dirty_string) def escape_markdown(self, md_string): # Don't mess with urls url_regex = re.compile( r'^(?:http|ftp)s?://' # http:// or https:// r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... r'localhost|' # localhost... r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) escaped_string = "" # Split the message into pieces, each "word" speparated into a string is a piece # Discord ignores formatting characters in urls so we can't just replace the whole # string... We need to go through words one by one to find out what is a url (don't) # escape) and what isn't (escape). for piece in md_string.split(" "): if url_regex.match(piece): escaped_string += "{} ".format(piece) continue # Absolutely needs to go first or it will replace our escaping slashes! piece = piece.replace("\\", "\\\\") piece = piece.replace("_", "\\_") piece = piece.replace("*", "\\*") escaped_string += "{} ".format(piece) if escaped_string.startswith(">"): escaped_string = "\\" + escaped_string escaped_string.strip() return escaped_string def strip_colour(self, dirty_string): colour_pattern = re.compile( u"\U000000A7" # selection symbol ".", flags=re.UNICODE) return colour_pattern.sub(r'', dirty_string) def setup_logging(self, level): if level.lower() == "debug": log_level = logging.DEBUG else: log_level = logging.INFO log_format = "%(asctime)s:%(name)s:%(levelname)s:%(message)s" logging.basicConfig(filename="bridge_log.log", format=log_format, level=log_level) stdout_logger = logging.StreamHandler(sys.stdout) stdout_logger.setFormatter(logging.Formatter(log_format)) logging.getLogger().addHandler(stdout_logger) def run_auth_server(self, port): # We need to import twisted after setting up the logger because twisted hijacks our logging from twisted.internet import reactor from .auth_server import AuthFactory # Create factory factory = AuthFactory() # Listen self.logger.info("Starting authentication server on port %d", port) factory.listen("", port) reactor.run(installSignalHandlers=False) def generate_random_auth_token(self, length): letters = string.ascii_lowercase + string.digits + string.ascii_uppercase return ''.join(random.choice(letters) for i in range(length)) def handle_disconnect(self, json_data=""): self.logger.info('Disconnected.') if json_data: self.logger.info("Disconnect json data: %s", json_data) if self.connection_retries >= self.config.failsafe_retries: self.logger.info( "Failed to join the server %s times in a row. Exiting.", self.connection_retries) self.logger.info( "Use a process supervisor if you wish to automatically restart the bridge." ) # This is possibly a huge hack... Since we cannot reliably raise exceptions on this thread # for them to be caught on the main thread, we call interrupt_main to raise a KeyboardInterrupt # on main and tell it to shut the bridge down. self.return_code = os.EX_TEMPFAIL _thread.interrupt_main() return self.previous_player_list = self.player_list.copy() self.accept_join_events = False self.player_list = bidict() if self.connection.connected: self.logger.info( "Forced a disconnection because the connection is still connected." ) self.connection.disconnect(immediate=True) time.sleep(15) while not self.is_server_online(): self.logger.info( 'Not reconnecting to server because it appears to be offline.') time.sleep(15) self.logger.info('Reconnecting.') self.connection_retries += 1 self.connection.connect() def handle_disconnect_packet(self, disconnect_packet): self.handle_disconnect(disconnect_packet.json_data) def minecraft_handle_exception(self, exception, exc_info): self.logger.error("A minecraft exception occured! %s:", exception, exc_info=exc_info) self.handle_disconnect() def is_server_online(self): server = MinecraftServer.lookup("{}:{}".format(self.config.mc_server, self.config.mc_port)) try: status = server.status() del status return True except ConnectionRefusedError: return False # AttributeError: 'TCPSocketConnection' object has no attribute 'socket' # This might not be required as it happens upstream except AttributeError: return False def register_handlers(self, connection): connection.register_packet_listener(self.handle_join_game, clientbound.play.JoinGamePacket) connection.register_packet_listener(self.handle_chat, clientbound.play.ChatMessagePacket) connection.register_packet_listener( self.handle_health_update, clientbound.play.UpdateHealthPacket) connection.register_packet_listener(self.handle_disconnect_packet, clientbound.play.DisconnectPacket) connection.register_packet_listener( self.handle_tab_list, clientbound.play.PlayerListItemPacket) connection.register_packet_listener( self.handle_player_list_header_and_footer_update, clientbound.play.PlayerListHeaderAndFooterPacket) def handle_player_list_header_and_footer_update(self, header_footer_packet): self.logger.debug("Got Tablist H/F Update: header=%s", header_footer_packet.header) self.logger.debug("Got Tablist H/F Update: footer=%s", header_footer_packet.footer) self.tab_header = json.loads(header_footer_packet.header)["text"] self.tab_footer = json.loads(header_footer_packet.footer)["text"] def handle_tab_list(self, tab_list_packet): self.logger.debug("Processing tab list packet") for action in tab_list_packet.actions: if isinstance( action, clientbound.play.PlayerListItemPacket.AddPlayerAction): self.logger.debug( "Processing AddPlayerAction tab list packet, name: %s, uuid: %s", action.name, action.uuid) username = action.name player_uuid = action.uuid if action.name not in self.player_list.inv: self.player_list.inv[action.name] = action.uuid else: # Sometimes we get a duplicate add packet on join idk why return if action.name not in self.uuid_cache.inv: self.uuid_cache.inv[action.name] = action.uuid # Initial tablist backfill if self.accept_join_events: webhook_payload = { 'username': username, 'avatar_url': "https://visage.surgeplay.com/face/160/{}".format( player_uuid), 'content': '', 'embeds': [{ 'color': 65280, 'title': '**Joined the game**' }] } for webhook in self.webhooks: self.req_future_session.post(webhook, json=webhook_payload) if self.config.es_enabled: self.es_logger.log_connection( uuid=action.uuid, reason=ConnectionReason.CONNECTED, count=len(self.player_list)) return else: # The bot's name is sent last after the initial back-fill if action.name == self.bot_username: self.accept_join_events = True if self.config.es_enabled: diff = set(self.previous_player_list.keys()) - set( self.player_list.keys()) for idx, player_uuid in enumerate(diff): self.es_logger.log_connection( uuid=player_uuid, reason=ConnectionReason.DISCONNECTED, count=len(self.previous_player_list) - (idx + 1)) # Don't bother announcing the bot's own join message (who cares) but log it for analytics still if self.config.es_enabled: self.es_logger.log_connection( uuid=action.uuid, reason=ConnectionReason.CONNECTED, count=len(self.player_list)) if self.config.es_enabled: self.es_logger.log_connection(uuid=action.uuid, reason=ConnectionReason.SEEN) if isinstance( action, clientbound.play.PlayerListItemPacket.RemovePlayerAction): self.logger.debug( "Processing RemovePlayerAction tab list packet, uuid: %s", action.uuid) username = self.mc_uuid_to_username(action.uuid) player_uuid = action.uuid webhook_payload = { 'username': username, 'avatar_url': "https://visage.surgeplay.com/face/160/{}".format( player_uuid), 'content': '', 'embeds': [{ 'color': 16711680, 'title': '**Left the game**' }] } for webhook in self.webhooks: self.req_future_session.post(webhook, json=webhook_payload) del self.uuid_cache[action.uuid] if action.uuid in self.player_list: del self.player_list[action.uuid] if self.config.es_enabled: self.es_logger.log_connection( uuid=action.uuid, reason=ConnectionReason.DISCONNECTED, count=len(self.player_list)) def handle_join_game(self, join_game_packet): self.logger.info('Connected and joined game as entity id %d', join_game_packet.entity_id) self.player_list = bidict() self.connection_retries = 0 def handle_chat(self, chat_packet): json_data = json.loads(chat_packet.json_data) if "extra" not in json_data: return chat_string = "" for chat_component in json_data["extra"]: chat_string += chat_component["text"] # Handle chat message regexp_match = re.match("<(.*?)> (.*)", chat_string, re.M | re.I) if regexp_match: username = regexp_match.group(1) original_message = regexp_match.group(2) player_uuid = self.mc_username_to_uuid(username) if username.lower() == self.bot_username.lower(): # Don't relay our own messages if self.config.es_enabled: bot_message_match = re.match( "<{}> (.*?): (.*)".format(self.bot_username.lower()), chat_string, re.M | re.I) if bot_message_match: self.es_logger.log_chat_message( uuid=self.mc_username_to_uuid( bot_message_match.group(1)), display_name=bot_message_match.group(1), message=bot_message_match.group(2), message_unformatted=chat_string) self.es_logger.log_raw_message( msg_type=chat_packet.Position.name_from_value( chat_packet.position), message=chat_packet.json_data) return self.logger.info( "Incoming message from minecraft: Username: %s Message: %s", username, original_message) self.logger.debug("msg: %s", repr(original_message)) message = self.escape_markdown( self.remove_emoji(original_message.strip().replace( "@", "@\N{zero width space}"))) webhook_payload = { 'username': username, 'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid), 'content': '{}'.format(message) } for webhook in self.webhooks: self.req_future_session.post(webhook, json=webhook_payload) if self.config.es_enabled: self.es_logger.log_chat_message( uuid=player_uuid, display_name=username, message=original_message, message_unformatted=chat_string) if self.config.es_enabled: self.es_logger.log_raw_message( msg_type=chat_packet.Position.name_from_value( chat_packet.position), message=chat_packet.json_data) def handle_health_update(self, health_update_packet): if health_update_packet.health <= 0: self.logger.debug("Respawned the player because it died") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN self.connection.write_packet(packet)
def main() -> int: # Handle program arguments ap = argparse.ArgumentParser( prog="mchat", description="A console chat client for most Minecraft server versions") ap.add_argument("server_address", help="IP address of a Minecraft server") ap.add_argument("-p", "--port", help="Minecraft server port (default: %(default)s)", type=int, default=25565) ap.add_argument("-u", "--username", help="Minecraft username or email") ap.add_argument( "-v", "--version", help="Client -> Server protocol version to use (default: %(default)s)", default="1.16.4") args = ap.parse_args() # Verify server version to keep the terminal looking clean if args.version not in SUPPORTED_MINECRAFT_VERSIONS.keys(): console.print( f"[bold yellow]{args.version} is not a valid Minecraft version. Versions from {list(SUPPORTED_MINECRAFT_VERSIONS.keys())[0]} to {list(SUPPORTED_MINECRAFT_VERSIONS.keys())[-1]} are allowed." ) return 1 # Do authentication if not args.username: username = Prompt.ask("Username or email") else: username = args.username password = getpass.getpass("Password: "******"[bright_black]Loaded authentication information") # Determine the actual protocol version number protocol_version_num = SUPPORTED_MINECRAFT_VERSIONS[args.version] console.print( f"[bright_black]Selecting protocol version {protocol_version_num}") # Authenticate with Mojang auth_token = AuthenticationToken() console.print(f"[bright_black]Contacting Yggdrasil...") try: auth_token.authenticate(username, password) except YggdrasilError as e: console.print(f"[bold red]Failed to authenticate Minecraft session") return 1 # Open a connection server_connection = Connection(args.server_address, args.port, auth_token, allowed_versions=[protocol_version_num]) try: server_connection.connect() except: console.print(f"[bold red]Could not connect to server") return 1 # Set up an incoming chat handler server_connection.register_packet_listener(incomingChatHandler, ChatMessagePacket) console.print(f"[bright_black]Listen to incoming chat packets") # Set up input loop console.print( "All further inputs will be sent to server chat. Press CTRL+C to stop") try: while True: # Get a line from the user chat_message = console.input() # Send the chat message packet = serverbound.play.ChatPacket() packet.message = chat_message server_connection.write_packet(packet) except KeyboardInterrupt as e: print("\rGoodbye") return 0
class bot: def __init__(self, username, password, bot_ign, reply_rate=20, whitelist=False): self.username = username self.password = password self.bot_ign = bot_ign self.debug = False self.whitelist = whitelist self.reply_rate = int(reply_rate) self.auth_token = authentication.AuthenticationToken() try: self.auth_token.authenticate(self.username, self.password) except YggdrasilError as error: print(error) exit() print("Logged in as %s." % self.auth_token.username) self.connection = Connection("mc.hypixel.net", 25565, auth_token=self.auth_token) self.command_delay = 0 self.msgQueue = [] self.partyQueue = [] self.commandQueue = [] self.msgCurrentChannel = "" self.party = {"inP": False, "from": "", "timestamp": 0} self.partyConfig = {} self.playercooldown = {} self.cooldownTimer = time.time() self.heartbeat = time.time() + 120 self.heartbeatCooldown = time.time() + 120 self.msgformat = msgformat.formats(self.bot_ign, 24) self.bots = {x: 0 for x in msgformat.bots if x != self.bot_ign} self.current_load = 0 self.inQueue = False self.inQueueTime = 0 self.muted = False self.muteDuration = 3600 self.unmutetime = 0 self.muteheartbeat = 0 self.leaderBuffer = [] self.mods = [] self.whitelisted = [] try: with open("whitelisted.txt", "r") as file: self.whitelisted = [x for x in file.read().split("\n")] except Exception: self.whitelisted = [] print("whitelisted loaded", len(self.whitelisted)) def initialize(self): self.connection.register_packet_listener( self.handle_join_game, clientbound.play.JoinGamePacket) self.connection.register_packet_listener( self.handle_chat, clientbound.play.ChatMessagePacket) self.connection.connect() def disconnect(self): self.msgQueue = [] self.partyQueue = [] self.friendQueue = [] self.connection.disconnect(True) exit() def send_chat(self, text, delay=0.6, bypass=False): if not self.inQueue or bypass: text = text[:255] # limit to 255 characters packet = serverbound.play.ChatPacket() packet.message = text self.connection.write_packet(packet) if self.debug: debugtext = "".join(x for x in text if x not in "-⛬⛫⛭⛮⛶_") print(debugtext) self.command_delay = time.time() time.sleep(delay * 1.05) def handle_join_game(self, packet): print(packet) self.heartbeat = time.time() - 50 self.heartbeatCooldown = time.time() - 50 time.sleep(0.5) self.send_chat("/p leave") print('Connected.') def handle_chat(self, chat_packet): try: chat_raw = str(chat_packet.json_data) chat_json = json.loads(chat_raw) msg = util.raw_to_msg(chat_json) if not (self.muted): if ("red" in chat_raw and len(msg) < 75 and "+]" not in msg) or self.debug: debugtext = "".join(x for x in msg if x not in "-⛬⛫⛭⛮⛶_") print(debugtext) if ("red" in chat_raw and len(msg) < 75 and "+]" not in msg): mutedetect = "".join(x for x in msg if x not in "-⛬⛫⛭⛮⛶_") if "Your mute will expire in" in mutedetect: muted = True duration = mutedetect[ mutedetect.index("Your mute will expire in") + 25:] duration = duration.split(' Find') del duration[1] duration = duration.pop(0) print(f'You are muted for {duration}.') if "extra" in chat_json: # On party request if chat_raw.count("/party accept") >= 2: for data in chat_json["extra"]: if "/party accept" in str(data): user = data["clickEvent"]["value"].split()[-1] if (user not in self.whitelisted ) and self.whitelist: return # whitelist if self.cooldowncheck(user, 5): return # cooldown self.partyQueue.append({ "mode": "queue", "user": user }) return return # On heartbeat elif "HeartBeat-KeepAlive" in chat_raw and "from" not in chat_raw.lower( ) and self.bot_ign in chat_raw: if time.time() - self.heartbeat > 70 and self.debug: self.debug = False self.heartbeat = time.time() onlinehb = ['bwstatsv2'] onlinehb.append([ int(time.time()), self.bot_ign, min( int( max(self.current_load, 1) / max(self.reply_rate, 0.1) * 100), 100) ]) onlinehb = [ x for x in onlinehb if time.time() - x[0] < 130 ] msgformat.bots = list( set([ x[1] for x in onlinehb if time.time() - x[0] < 130 ])) self.bots = {} for bot in onlinehb: if bot[1] in self.bots: self.bots[bot[1]] = max( bot[2], self.bots[bot[1]]) else: self.bots[bot[1]] = bot[2] if self.debug: for bot in self.bots: print(bot, self.bots[bot], "%") # print(msgformat.bots) return elif "Party Leader" in chat_raw and "●" in chat_raw: leader = [ leader for leader in msg[msg.index(":") + 1:].split("●") if len(leader) > 1 ] leader = [leader.split()[-1] for leader in leader] leader = leader.pop(0) self.leaderBuffer.append(leader) elif "Party Moderators" in chat_raw and "●" in chat_raw: mods = [ mods for mods in msg[msg.index(":") + 1:].split("●") if len(mods) > 1 ] mods = [mods.split()[-1] for mods in mods] self.mods.append(self.leaderBuffer[0]) # On party list return elif "Party Members" in chat_raw and "●" in chat_raw: # Party members (2): [VIP] MinuteBrain ● [MVP+] Its_Me_Me ● users = [ user for user in msg[msg.index(":") + 1:].split("●") if len(user) > 1 ] users = [user.split()[-1] for user in users] # remove ranks users.append(self.leaderBuffer[0]) users.extend(self.mods) self.leaderBuffer = [] self.mods = [] users.remove(self.bot_ign) # remove bot from the list self.partyQueue = [{ "mode": "list", "user": users }] + self.partyQueue # put on top of the queue return # On msg request elif ("From " in chat_raw) and ("light_purple" in chat_raw) and (self.bot_ign not in chat_raw): self.chat_msg(msg) return # On open PM channel elif { "text": " for the next 5 minutes. Use ", "color": "green" } in chat_json["extra"]: user = msg[msg.index("with") + 4:msg.index("for")].split()[-1] #print(user) self.msgCurrentChannel = user return # On friend request elif ("Click to" in chat_raw) and ("/f accept " in chat_raw): for data in chat_json["extra"]: if "/f accept " in str(data).lower(): user = data["clickEvent"]["value"].split()[-1] if self.cooldowncheck(user, 2): return # cooldown self.commandQueue.append({ "command": "friend_request", "user": user }) return return # On queue elif ("The game starts in" in chat_raw) or ( "has joined" in msg and "/" in msg ) or ("has quit" in msg and "/" in msg) or ("The party leader, " in chat_raw and "yellow" in chat_raw): if not (self.inQueue ) and time.time() - self.inQueueTime > 5: self.inQueue = True self.inQueueTime = time.time() print("Blocked - " + self.party["from"]) self.cooldowncheck(self.party["from"], 60) self.party["inP"] = True self.party["timestamp"] = time.time() + 99999 time.sleep(1) self.party["inP"] = True self.send_chat("/pchat :( hey! don't warp me!", 0.07, True) self.send_chat("/p leave", 1, True) self.send_chat("/hub bw", 0.7, True) for _ in range(15): self.send_chat("/golimbo", 0.07, True) self.party["inP"] = True self.party["timestamp"] = time.time() + 5 self.inQueue = False self.inQueueTime = time.time() return # On whereami respond elif "You are currently connected to server" in msg and "aqua" in chat_raw.lower( ): if "lobby" in msg: self.commandQueue.append({"command": "in_lobby"}) else: self.commandQueue.append({"command": "in_game"}) return else: # while muted print('idk ur muted or smth') except Exception as error_code: print("chat handle error!!", error_code) def chat_msg(self, msg): # >>> msg = 'From [MVP+] FatDubs: FatDubs gamerboy80 5' if "+send" not in msg.lower(): msg = "".join([ char for char in msg if char.lower() in "[]:abcdefghijklmnopqrstuvwxyz0123456789_ +/" ]) msg = " ".join(msg.split()) # remove double space msg = msg.replace("+ ", "+").replace("++", "+").replace("+]", "]") user = msg[:msg.index(":")].split()[-1] if (user not in self.whitelisted) and self.whitelist: return # whitelist agus = msg[msg.index(":") + 1:].split() # user = '******' # agus = ['FatDubs', 'gamerboy80', '5'] mode = 0 # stats mode if len(agus) > 1 and "+send" not in "".join(agus).lower(): mode = agus[-1] if mode in [str(x) for x in range(6)]: mode = int(mode) agus.pop(-1) else: mode = 0 # user = '******' # agus = ['FatDubs', 'gamerboy80'] # mode = 5 if self.cooldowncheck(user, 1) and user.lower() not in ["fatdubs"]: return # player cooldown # commands if "+" in msg: command = agus[0] if command.lower() == "+send" and user.lower() in [ "fatdubs" ] and len(agus) >= 2: self.commandQueue.append({ "command": "send_command", "send": " ".join(agus[1:]) }) elif command.lower() == "+limbo" and user.lower() in ["fatdubs"]: print("Warp to Limbo") for _ in range(15): self.send_chat("/golimbo", 0.07, True) self.send_chat("/whereami") elif command.lower() == "+whitelist" and user.lower() in [ "fatdubs" ] and len(agus) >= 2: self.whitelistChange = ("".join(agus[1:])) print('whitelist queue - ' + self.whitelistChange) elif command.lower() == "+stop" and user.lower() in ["fatdubs"]: print('disconnecting..') self.disconnect() elif command.lower() in ["+pmode", "+setpartymode"]: self.partyConfig[user] = mode self.msgQueue = [{ "msgMode": "party_mode", "user": user, "mode": mode }] + self.msgQueue elif command.lower() == "+resetcooldown" and user.lower() in [ "fatdubs" ]: self.playercooldown = {} print('reset cooldown') elif command.lower() in ["+reload", "+reloadall"] and user.lower( ) in ["fatdubs"] + [x.lower() for x in list(self.bots)]: print("Reloading...") try: reload(msgformat) self.msgformat = msgformat.formats(self.bot_ign, 24) self.bots = { x: 0 for x in msgformat.bots if x != self.bot_ign } except Exception: print("Fail to reload msgformat") try: reload(hypixelapi) except Exception: print("Fail to reload hypixelapi") elif command.lower() == "+debug" and user.lower() in ["fatdubs"]: self.debug = not (self.debug) print(f"Debug : {self.debug}") else: self.msgQueue = [{ "msgMode": "wrong_syntax", "user": user }] + self.msgQueue return # stats request if self.current_load <= self.reply_rate: if len(agus) > 0: if len(agus[0]) <= 16: if len(agus) == 1: self.msgQueue = [{ "msgMode": "stats", "replyto": user, "username": agus[0], "mode": mode }] + self.msgQueue elif len(agus) > 1 and len(agus) <= 4: self.msgQueue = [{ "msgMode": "stats_multiple", "replyto": user, "username": agus, "mode": mode }] + self.msgQueue else: self.msgQueue = [{ "msgMode": "wrong_syntax", "user": user }] + self.msgQueue else: self.msgQueue = [{ "msgMode": "wrong_syntax", "user": user }] + self.msgQueue else: self.msgQueue = [{ "msgMode": "wrong_syntax", "user": user }] + self.msgQueue def cooldowncheck(self, user, n=1): if user not in self.playercooldown: self.playercooldown[user] = n else: if self.playercooldown[user] > 6: self.playercooldown[user] += 3 else: self.playercooldown[user] += n if self.playercooldown[user] > 100 and user.lower() not in ["fatdubs"]: self.commandQueue.append({"command": "ignore", "user": user}) print("Ignored", user, self.playercooldown[user]) return True elif self.playercooldown[user] > 6 and user.lower() not in ["fatdubs"]: print("Reject spam from", user, self.playercooldown[user]) return True else: self.current_load += 1 return False def cooldown_tick(self): if time.time() - self.cooldownTimer >= 6: self.cooldownTimer = time.time() for user in list(self.playercooldown): self.playercooldown[user] -= 1 self.playercooldown = { x: self.playercooldown[x] for x in list(self.playercooldown) if self.playercooldown[x] > 0 } def msg_tick(self): if len(self.msgQueue) > 0: currentQueue = self.msgQueue.pop(0) if currentQueue["msgMode"] == "stats": replyTo = currentQueue["replyto"] username = currentQueue["username"] if currentQueue["username"].lower() == "me": username = currentQueue["replyto"] mode = currentQueue["mode"] if self.msgCurrentChannel != replyTo: while time.time() - self.command_delay < 0.5: time.sleep(0.05) self.send_chat("/r", 0) data = hypixelapi.getPlayer(username, hypixelapi.nextKey()) raw = hypixelapi.convert(data, mode, "msg") msg = self.msgformat.msg(raw, replyTo.lower() == username.lower()) while time.time() - self.command_delay < 0.7: time.sleep(0.05) if replyTo.lower() == self.msgCurrentChannel.lower(): print(f"(R) {replyTo} --> {username}") self.send_chat(msg, 0.4) else: if hypixelapi.getPlayer( replyTo, hypixelapi.nextKey())["msgsetting"]: print(f"(MSG) {replyTo} --> {username}") self.send_chat(f"/msg {replyTo} " + msg, 0.4) else: print(f"(MSG) Couldn't reply to {replyTo}") self.msgCurrentChannel = "" if currentQueue["msgMode"] == "stats_multiple": replyTo = currentQueue["replyto"] usernames = currentQueue["username"] mode = currentQueue["mode"] #util.dict_increment(self.quotaChange,replyTo,len(usernames)) if self.msgCurrentChannel != replyTo: while time.time() - self.command_delay < 0.6: time.sleep(0.05) self.send_chat("/r", 0) handle = util.multithreading(usernames, mode) handle.start() raws = [handle.output[x] for x in list(handle.output)] msg = list(self.msgformat.party(raws, mode))[0] msg = msgformat.insertInvis(msg, 20) while time.time() - self.command_delay < 0.7: time.sleep(0.05) if replyTo.lower() == self.msgCurrentChannel.lower(): print(f"(R) {replyTo} --> {usernames}") self.send_chat(msg, 0.4) else: if hypixelapi.getPlayer( replyTo, hypixelapi.nextKey())["msgsetting"]: print(f"(MSG) {replyTo} --> {username}") self.send_chat(f"/msg {replyTo} " + msg, 0.4) else: print(f"(MSG) Can't send msg {replyTo}") self.msgCurrentChannel = "" elif currentQueue["msgMode"] == "wrong_syntax": print(f"Wrong_syntax: {currentQueue['user']}") while time.time() - self.command_delay < 0.5: time.sleep(0.05) self.send_chat("/r " + self.msgformat.wrong_syntax(), 0.5) elif currentQueue["msgMode"] == "party_mode": print( f"Party Mode: {currentQueue['user']} --> {currentQueue['mode']}" ) while time.time() - self.command_delay < 0.5: time.sleep(0.05) self.send_chat( "/r " + self.msgformat.party_mode(currentQueue["mode"]), 0.5) def party_chat_transit(self, msg, delay=0.5): while len(self.msgQueue) > 0: self.msg_tick() time.sleep(0.05) while time.time() - self.command_delay < delay: time.sleep(0.05) self.send_chat(msg, delay) self.party["timestamp"] = time.time() def party_tick(self): if len(self.partyQueue) > 0 and len(self.msgQueue) == 0: currentQueue = self.partyQueue.pop(0) if currentQueue["mode"] == "queue" and self.party[ "inP"]: # requeue if in party #print("Party Requeued !!") self.partyQueue.append(currentQueue) else: if currentQueue["mode"] == "queue": self.party = {"inP": True, "from": currentQueue["user"]} #util.dict_increment(self.quotaChange,self.party["from"],1) while time.time() - self.command_delay < 0.5: time.sleep(0.05) # prevent sending command too fast self.party_chat_transit(f"/p accept {self.party['from']}", 0.4) self.party_chat_transit(f"/pl", 0.3) elif currentQueue["mode"] == "list": users = currentQueue['user'] print("Party list -", " ".join(users)) for user in users: if user.lower() != self.bot_ign and user in list( self.bots): print("multiple bots in party!!!!!") self.cooldowncheck(self.party["from"], 20) while time.time() - self.command_delay < 2: self.msg_tick() time.sleep(0.05) self.send_chat("/p leave") self.party["inP"] = False return if len(users) <= self.msgformat.party_max: if self.party["from"] in self.partyConfig: mode = self.partyConfig[self.party["from"]] else: mode = 0 handle = util.multithreading(users, mode) handle.start() raws = [handle.output[x] for x in list(handle.output)] msgs = self.msgformat.party(raws, mode) while time.time() - self.command_delay < 0.3: time.sleep(0.05) for msg in msgs: self.party_chat_transit(f"/pchat {msg}", 0.3) else: while time.time() - self.command_delay < 0.3: time.sleep(0.05) self.party_chat_transit( "/pchat " + self.msgformat.party_too_large(), 0.3) while time.time() - self.command_delay < 1: self.msg_tick() time.sleep(0.05) self.send_chat("/p leave") self.party["inP"] = False if self.party["inP"] and time.time() - self.party["timestamp"] > 2: print("Party timeout", self.party["from"]) while time.time() - self.command_delay < 0.8: time.sleep(0.05) self.send_chat("/p leave", 0.3) self.party["inP"] = False def command_tick(self): if len(self.commandQueue) > 0: currentQueue = self.commandQueue.pop(0) if currentQueue["command"] == "friend_request": print(f"Friend accepted - {currentQueue['user']}") while time.time() - self.command_delay < 0.7: time.sleep(0.05) self.send_chat(f"/f accept {currentQueue['user']}", 0.3) elif currentQueue["command"] == "send_command": print(f"Command sent - {currentQueue['send']}") while time.time() - self.command_delay < 0.7: time.sleep(0.05) self.send_chat(currentQueue["send"], 0.3, True) elif currentQueue["command"] == "in_game": print("Warp to Lobby") while time.time() - self.command_delay < 0.5: time.sleep(0.05) self.send_chat("/hub bw", 0.3, True) elif currentQueue["command"] == "in_lobby": print("Warp to Limbo") for _ in range(15): self.send_chat("/golimbo", 0.07, True) self.send_chat("/whereami") elif currentQueue["command"] == "ignore": print(f"Ignored - {currentQueue['user']}") while time.time() - self.command_delay < 0.7: time.sleep(0.05) self.send_chat(f"/ignore add {currentQueue['user']}") def heartbeat_tick(self): if time.time() - self.heartbeat > 610: self.connection.disconnect(True) raise Exception("No heartbeat detect! Reconnecting") return if time.time() - self.heartbeat > 120 and not (self.debug): #self.debug = True self.connection.connect() #print("Debug : True") print("Reconnecting") if time.time() - self.heartbeat > 60 and time.time( ) - self.heartbeatCooldown > 30: heartbeat_length = time.time() - self.heartbeat random_msg = "".join( [chr(random.randint(64, 125)) for _ in range(30)]) while time.time() - self.command_delay < 0.5: time.sleep(0.7) self.send_chat( f"/msg {self.bot_ign} HeartBeat-KeepAlive {random_msg}", 0.3) #comment this to test heartbeat restart system. self.heartbeatCooldown = time.time() self.send_chat("/whereami", 0.2) if self.current_load > self.reply_rate: print("Overloaded!! <-----") self.current_load = 0 if time.time() - self.heartbeat > 300: self.connection.connect() print("Reconnecting") print(f"Heartbeat ({int(heartbeat_length)}sec)") return def tick(self): self.heartbeat_tick() try: self.party_tick() self.msg_tick() self.command_tick() self.cooldown_tick() except Exception as error_code: print("Tick error! (skiped) -", error_code)
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection(options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it. return print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet, output="default"): if output == "raw": print( "Message (%s): %s" % (chat_packet.field_string('position'), chat_packet.json_data)) chat_json = json.loads(chat_packet.json_data) if output == "pretty": print( json.dumps(chat_json, sort_keys=True, indent=4, separators=(',', ': '))) if output == "default": message = Message(chat_json) ts = datetime.datetime.fromtimestamp( time.time()).strftime('%Y-%m-%d %H:%M:%S') print("[{}] {}".format(colored(ts, "grey"), message.formatted_str)) connection.register_packet_listener(print_chat, clientbound.play.ChatMessagePacket) connection.connect() def requeue(realm): message = "/joinqueue " + realm packet = serverbound.play.ChatPacket() packet.message = message print(message) connection.write_packet(packet) # Re-join realm every 60 seconds rt = RepeatedTimer(60, requeue, options.realm) while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: rt.stop() print("Bye!") sys.exit()
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection(options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it. return print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) # debug chat callback def print_chat(chat_packet): print("Message (%s): %s" % (chat_packet.field_string('position'), chat_packet.json_data)) # lists serves as output message queues outQueue = [] # boolean to tell if we are running running = True # console input callback def input_thread(): while running: text = input() outQueue.append(text) time.sleep(0.1) # helper function to insert into message queue def insert_into_queue(message, player, globalFlag): if globalFlag: outQueue.append(message) else: outQueue.append('/msg ' + player + ' ' + message) # helper function to handle commands def process_message(message, player, playerId, globalFlag): if message == '$whoami': insert_into_queue('You are ' + player + '!', player, globalFlag) if message == '$selling': try: dealFile = open('deals.json', 'r') deals = json.loads(dealFile.read()) dealFile.close() items = [] for item in deals['selling']: items.append(item['item']) insert_into_queue('Selling these items: ' + str(items), player, globalFlag) except: insert_into_queue( 'Sorry! Deals are not available at this time.', player, globalFlag) if message == '$buying': try: dealFile = open('deals.json', 'r') deals = json.loads(dealFile.read()) dealFile.close() insert_into_queue( 'Buying these items: ' + str(deals['buying']), player, globalFlag) except: insert_into_queue( 'Sorry! Deals are not available at this time.', player, globalFlag) if message == '$reps': try: dealFile = open('deals.json', 'r') deals = json.loads(dealFile.read()) dealFile.close() outQueue.append('Representatives of Astara: ' + str(deals['representatives'])) except: insert_into_queue( 'Sorry! Database is not available at this time.', player, globalFlag) if message == '$help': insert_into_queue('Hi! I\'m the Astaran Trade Bot. Minimum trade value is 1db.' + \ ' Here are some commands you can use: $selling, $buying, $price <item>, ' + \ '$whoami, $reps, $help', player, globalFlag) if message.startswith('$price '): try: query = message[7:] dealFile = open('deals.json', 'r') deals = json.loads(dealFile.read()) dealFile.close() name = '' price = '' for item in deals['selling']: for alias in item['alias']: if query.lower() == alias.lower(): name = item['item'] price = item['price'] break if name != '': break if name == '' or price == '': insert_into_queue('Sorry! No price listed for that item.', player, globalFlag) else: insert_into_queue('Astara sells ' + name + ' for ' + price, player, globalFlag) except: insert_into_queue('Sorry! No price listed for that item.', player, globalFlag) # chat processing callback def process_chat(chat_packet): position = chat_packet.field_string('position') if position == 'CHAT' or position == 'SYSTEM': data = json.loads(chat_packet.json_data) # Global Chat if data['translate'] == 'chat.type.text': # grab useful data from json message = data['with'][1] player = data['with'][0]['insertion'] hoverStr = data['with'][0]['hoverEvent']['value']['text'] start = hoverStr.index('id:\"') + 4 end = hoverStr.index('\",type:') playerId = hoverStr[start:end] # print chat message outStr = playerId + ' (' + player + '): ' + message print(outStr) # log message log = open('log.txt', 'a') log.write(outStr + '\n') log.close() # process message process_message(message, player, playerId, True) # Private Chat if data['translate'] == 'commands.message.display.incoming': # grab useful data from json message = data['with'][1]['text'] player = data['with'][0]['insertion'] hoverStr = data['with'][0]['hoverEvent']['value']['text'] start = hoverStr.index('id:\"') + 4 end = hoverStr.index('\",type:') playerId = hoverStr[start:end] # print chat message outStr = playerId + ' (' + player + ') PRIVATE: ' + message print(outStr) # log message log = open('log.txt', 'a') log.write(outStr + '\n') log.close() # process message process_message(message, player, playerId, False) # Register Debug callback #connection.register_packet_listener( # print_chat, clientbound.play.ChatMessagePacket) # Register our chatbot logic connection.register_packet_listener(process_chat, clientbound.play.ChatMessagePacket) # start network thread connection.connect() # start console thread inThread = threading.Thread(target=input_thread) inThread.start() # Main bot console loop while running: try: time.sleep(0.05) if len(outQueue) != 0: msg = outQueue.pop() if msg == '/respawn': print('Respawning...') packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) elif msg == '/exit': print('Disconnecting') running = False else: print('Sent Message: ' + msg) packet = serverbound.play.ChatPacket() packet.message = msg connection.write_packet(packet) except KeyboardInterrupt: outQueue.append('/exit')
class Minecraft(): def __init__(self, username, password=None, server=None, versions=("1.12.2", "1.12.2"), auto_connect=False): self.username = username self.password = password self.server = server self.versions = versions self.event = lambda x: print(x) self.auth_token = authentication.AuthenticationToken( username=self.username, access_token=self.password) print("authenticated: %s" % self.auth_token.authenticate( self.username, self.password, invalidate_previous=False)) self.connection = Connection(self.server, auth_token=self.auth_token, allowed_versions=self.versions) self.connection.register_packet_listener(lambda x: self.print_chat(x), ChatMessagePacket) print(self.auth_token) print(self.connection) if auto_connect: self._loop_reconect() def _loop_reconect(self): Timer(30.0, self._loop_reconect).start() try: self.connect() except: pass def add_chat_event(self, event): self.event = event def connect(self): self.connection.connect() def disconnect(self, immediate=False): self.connection.disconnect(immediate=immediate) def send_message(self, message): if message.strip() == "" or message == None: return False packet = ChatPacket() packet.message = message self.connection.write_packet(packet) def print_chat(self, chat_packet): try: a = json.loads(chat_packet.json_data) except Exception as e: pass else: self.event(self.parse_chat(a)) def parse_chat(self, chat_data): try: chat_data["extra"][0]["extra"] try: return (True, self.parse_chat(chat_data["extra"][0])[1]) except Exception as e: print(e) except: try: return (False, "".join([x["text"] for x in chat_data["extra"]])) except: pass return
def main(): options = get_options() assets = AssetsManager(options.assets) mcdata = DataManager("./mcdata") if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username, allowed_versions=[options.mcversion]) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) return print("Logged in as %s..." % auth_token.username) connection = Connection(options.address, options.port, auth_token=auth_token, allowed_versions=[options.mcversion]) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it. return if type(packet) in [ clientbound.play.EntityVelocityPacket, clientbound.play.EntityLookPacket ]: # Prevents useless console spam return print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) chat = ChatManager(assets) chat.register(connection) chunks = ChunksManager(mcdata) chunks.register(connection) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) connection.connect() while True: try: text = input() if text.startswith("!"): if text == "!respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) elif text.startswith("!print "): p = text.split(" ") chunks.print_chunk( chunks.get_chunk(int(p[1]), int(p[2]), int(p[3])), int(p[4])) elif text == "!chunks": area = chunks.get_loaded_area() y_count = area[1][1] - area[0][1] print("Bounds: %s" % (area, )) for y in range(area[0][1], area[1][1]): print("Slice %d:" % (y)) for z in range(area[0][2], area[1][2]): for x in range(area[0][0], area[1][0]): if (x, y, z) in chunks.chunks: c = 'X' else: c = '.' print(c, end="") print() elif text == "!export": area = chunks.get_loaded_area(True) export_area(area[0][0] * 16, area[0][1] * 16, area[0][2] * 16, area[1][0] * 16, area[1][1] * 16, area[1][2] * 16, chunks, assets, mcdata) else: print("Unknow test command: %s" % (text)) else: chat.send(connection, text) except KeyboardInterrupt: print("Bye!") sys.exit() except Exception as ex: print("Exception: %s" % (ex)) traceback.print_exc()
def main(): """Our main function for running the simple pyCraft implementation. This function handles and maintains: - Gaining authentication tokens & 'logging in' - Connecting to the provided server, online or offline - Prints the chat packet data to standard out on Clientbound Packet - Writes Serverbound chat Packets when required - Dumping all packets to standard out Notes ----- This is a blocking function. """ options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection(options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it # unless explicitly requested by the user. if options.dump_unknown: print("--> [unknown packet] %s" % packet, file=sys.stderr) else: print("--> %s" % packet, file=sys.stderr) def print_outgoing(packet): print("<-- %s" % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print("Connected.") connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet): print("Message (%s): %s" % (chat_packet.field_string("position"), chat_packet.json_data)) connection.register_packet_listener(print_chat, clientbound.play.ChatMessagePacket) connection.connect() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection(options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it. return print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) def print_raw_json(chat_packet): print("Message (%s): %s" % (chat_packet.field_string('position'), chat_packet.json_data)) def print_raw_text(chat_packet): print("Message (%s): " % chat_packet.field_string('position'), end="") printRawText(json.loads(chat_packet.json_data)) print("") def print_color_text(chat_packet): print("Message (%s): " % chat_packet.field_string('position'), end="") printColorText(json.loads(chat_packet.json_data)) print("") def keep_alive(packet): print("keep_alive[%d] packet receive." % packet.keep_alive_id) from random import randint ret = serverbound.play.KeepAlivePacket() ret.keep_alive_id = randint(0, 10000) connection.write_packet(ret) print("Write packet ok.") connection.register_packet_listener(print_color_text, clientbound.play.ChatMessagePacket) connection.register_packet_listener(keep_alive, clientbound.play.KeepAlivePacket) connection.connect() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
class Player: __retries = 0 def __init__(self, account: str, password: str, server_address: str, port: int, version: int, auto_reconnect: bool, auto_respawn: bool, lang: Lang): self.__email = account self.__password = base64.b64encode(password.encode()) self.__lang = lang self.__logger = logging.getLogger("Auth") logging.basicConfig(level=logging.INFO) tokens = self.__get_tokens() self.__auth = authentication.AuthenticationToken( username=self.__email, access_token=tokens["access"], client_token=tokens["client"]) self.auth() self.__auto_reconnect = auto_reconnect self.__auto_respawn = auto_respawn self.__connection = Connection(address=server_address, port=port, initial_version=version, auth_token=self.__auth) if not self.__auth.authenticated: return self.username = self.__auth.profile.name self.__logger = logging.getLogger(self.username) self.__connection.register_packet_listener( self.handle_join_game, clientbound.play.JoinGamePacket) self.__connection.register_packet_listener( self.print_chat, clientbound.play.ChatMessagePacket) self.__connection.register_packet_listener( self.handle_disconnect, clientbound.play.DisconnectPacket) self.__connection.register_packet_listener( self.handle_health_change, clientbound.play.UpdateHealthPacket) self.__connection.register_exception_handler(self.handle_exception) try: self.__connection.connect() except Exception as e: self.__logger.error(str(e)) self.__retry() # def connect(self, ip, port): # self.__init(self.username Connection) def __get_tokens(self) -> dict: try: with open('./data.json', 'r') as fs: auth = json.load(fs) except FileNotFoundError: return {"access": None, "client": None} else: if self.__email in auth: return auth[self.__email] else: return {"access": None, "client": None} def __refresh_tokens(self, access: str, client: str): auth = {} try: with open('./data.json', 'r') as fs: auth = json.load(fs) except FileNotFoundError: pass finally: auth[self.__email] = {"access": access, "client": client} with open('./data.json', 'w') as fs: json.dump(auth, fs, indent=2) def auth(self): try: self.__auth.refresh() except YggdrasilError: self.__login() except ValueError: self.__login() else: self.__logger.info( self.__lang.lang("main.auth.still_valid").format( email=self.__email)) self.__refresh_tokens(access=self.__auth.access_token, client=self.__auth.client_token) def __login(self): self.__logger.info( self.__lang.lang("main.auth.login").format(email=self.__email)) try: self.__auth.authenticate(username=self.__email, password=base64.b64decode( self.__password).decode()) except YggdrasilError as e: self.__logger.error( self.__lang.lang("main.auth.error").format(email=self.__email, message=str(e))) else: self.__refresh_tokens(access=self.__auth.access_token, client=self.__auth.client_token) def reconnect(self): try: self.__connection.connect() except Exception as e: self.__logger.error(str(e)) self.__retry() # noinspection PyUnusedLocal def handle_join_game(self, join_game_packet): self.__logger.info( self.__lang.lang("player.connected").format( server=self.__connection.options.address, port=self.__connection.options.port)) self.__retries = 0 packet = serverbound.play.ClientSettingsPacket() packet.locale = self.__lang.lang_name packet.view_distance = 10 packet.chat_mode = packet.ChatMode.FULL packet.chat_colors = False packet.displayed_skin_parts = packet.SkinParts.ALL packet.main_hand = AbsoluteHand.RIGHT self.__connection.write_packet(packet) def print_chat(self, chat_packet): self.__logger.info("[{position}] {message}".format( position=chat_packet.field_string('position'), message=self.__lang.parse_json(json.loads(chat_packet.json_data)))) def handle_disconnect(self, disconnect_packet): self.__logger.warning( self.__lang.lang("player.connection.lost").format( reason=self.__lang.parse_json( json.loads(disconnect_packet.json_data)))) if self.__auto_reconnect: self.__retry() def handle_health_change(self, health_packet): self.__logger.warning( self.__lang.lang("player.health.changed").format( health=str(health_packet.health), food=str(health_packet.food), saturation=str(health_packet.food_saturation))) if self.__auto_respawn and health_packet.health == 0: self.__logger.info(self.__lang.lang("player.respawn.hint")) timer = threading.Timer(1.0, self.respawn) timer.start() def handle_exception(self, e, info): if type(info[1]) == LoginDisconnect: message = str(e).replace( 'The server rejected our login attempt with: "', '').replace('".', '') try: self.__logger.error( self.__lang.lang("player.connection.rejected").format( reason=self.__lang.parse_json(json.loads(message)))) except json.decoder.JSONDecodeError: self.__logger.error( self.__lang.lang("player.connection.rejected").format( reason=message)) elif type(info[1]) == YggdrasilError: self.__logger.error(self.__lang.lang("player.session.expired")) self.auth() timer = threading.Timer(1.0, self.reconnect) timer.start() return else: self.__logger.error("{type}: {message}".format(type=type(info[1]), message=str(e))) if self.__auto_reconnect: if not self.__connection.connected: self.__retry() def __retry(self): self.__retries += 1 if self.__retries >= 6: self.__retries = 0 return self.__logger.info( self.__lang.lang("player.connection.retry").format( times=str(self.__retries))) timer = threading.Timer(5.0, self.reconnect) timer.start() def respawn(self): packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN self.__connection.write_packet(packet) self.__logger.info(self.__lang.lang("player.respawned")) def disconnect(self): self.__connection.disconnect() self.__logger.info(self.__lang.lang("player.disconnected")) def toggle_auto_respawn(self): self.__auto_respawn = not self.__auto_respawn self.__logger.info( self.__lang.lang("player.auto_respawn.toggle").format( value=self.__auto_respawn)) def toggle_auto_reconnect(self): self.__auto_reconnect = not self.__auto_reconnect self.__logger.info( self.__lang.lang("player.auto_reconnect.toggle").format( value=self.__auto_reconnect)) def chat(self, text: str): if text == "": return packet = serverbound.play.ChatPacket() packet.message = text self.__connection.write_packet(packet)
connection.register_packet_listener( handle_join_game, clientbound.play.JoinGamePacket) connection.register_packet_listener( print_chat, clientbound.play.ChatMessagePacket) connection.register_packet_listener( sound, clientbound.play.SoundEffectPacket) connection.connect() time.sleep(3) useitem() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection( options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection( options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it. return print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener( print_incoming, Packet, early=True) connection.register_packet_listener( print_outgoing, Packet, outgoing=True) once = False def handle_join_game(join_game_packet): message_queue.append(("CONNECTION", "**Connected**")) once = True print('Connected.') connection.register_packet_listener( handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet): print("[%s]: %s" % ( chat_packet.field_string('position'), parse_chat_item(json.loads(chat_packet.json_data)))) connection.register_packet_listener( print_chat, clientbound.play.ChatMessagePacket) # Add a deque for chat messages and register a method to get them message_queue = deque() def forward_chat(chat_packet): msg = parse_chat_item(json.loads(chat_packet.json_data)) if msg.startswith("<"): author, message = parse_message(msg) if (author != auth_token.username and message != ""): # Don't put in queue your own messages! message_queue.append((author, message)) connection.register_packet_listener( forward_chat, clientbound.play.ChatMessagePacket) # More maybe? Add here shit for a chatbot # Auto respawn because we can't send chat while dead def auto_respawn(update_health_packet): if update_health_packet.health <= 0: print("Respawning") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) connection.register_packet_listener( auto_respawn, clientbound.play.UpdateHealthPacket) # Start the discord thread and provide the message deque botThread = DiscordBotThread(message_queue, connection) botThread.daemon = True botThread.start() connection.connect() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
def main(): args = sys.argv[1:] if (len(args) != 1): print("Uso : bot.py queue\nEx: bot.py towny") sys.exit() else: EMAIL = "" PASSWORD = "" MULTIMC_INSTANCE = "" auth = authentication.AuthenticationToken() try: auth.authenticate(EMAIL, PASSWORD) except YggdrasilError as e: print(e) sys.exit() print("Logado como %s" % auth.username) connection = Connection("dc-f626de6d73b7.earthmc.net", 25577, auth_token=auth, allowed_versions=[477]) def entra_server(join_game_packet): print("Conectado no servidor") packet = serverbound.play.ChatPacket() packet.message = ("/joinqueue %s" % (args[0])) connection.write_packet(packet) connection.register_packet_listener(entra_server, clientbound.play.JoinGamePacket) def mensagem(chat_packet): if (chat_packet.field_string( 'position' ) == "SYSTEM" and chat_packet.json_data.startswith( '{"extra":[{"color":"yellow","text":"You are currently in position ' )): result = queue_message_from_dict( json.loads(chat_packet.json_data)) print( "Pos : (%s) %s [%s]" % (result.extra[1].text[:-1], result.extra[3].text, args[0])) if (int(result.extra[1].text[:-1].replace(" ", ""), 10) <= 5): connection.disconnect() os.system('multimc -l "%s"' % (MULTIMC_INSTANCE)) elif (chat_packet.field_string('position') == "CHAT"): result = chat_message_from_dict( json.loads(chat_packet.json_data)) print("[%s] %s > %s" % (result.extra[0].hover_event.value[0].extra[1].text[:-1], result.extra[0].extra[0].text[:-2], result.extra[1].extra[0].text)) elif (chat_packet.field_string('position') != "SYSTEM"): print("UNHANDLED %s MESSAGE : \n(%s)" % (chat_packet.field_string('position'), chat_packet.json_data)) connection.register_packet_listener(mensagem, clientbound.play.ChatMessagePacket) connection.connect() while True: try: text = input() if text == "/respawn": print("Respawnando ...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Tchau!") sys.exit()
def main(): auth_token = authentication.AuthenticationToken() try: with open('minecraft.auth', 'r') as f: auth_token.client_token, auth_token.access_token = f.read().splitlines() # Library has issues need to do some hackey stuff to make sure it works. # I would use validate, but that would require some rewriting as well. auth_token.username = "******" auth_token.refresh() except (IOError, YggdrasilError): # IF there is no authentication file authenticate using username and password try: options = get_options() auth_token.authenticate(options["username"], options["password"]) except YggdrasilError as e: print(e) sys.exit() with open('minecraft.auth', 'w') as fout: fout.write(auth_token.client_token + '\n') fout.write(auth_token.access_token) print("Logged in as %s..." % auth_token.username) connection = Connection( "localhost", 25565, auth_token=auth_token) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener( handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet): print("Message (%s): %s" % ( chat_packet.field_string('position'), chat_packet.json_data)) connection.register_packet_listener( print_chat, clientbound.play.ChatMessagePacket) global connected connected = True def disconnect(disconnect_packet): print("You were disconnected: %s" % disconnect_packet.json_data) global connected connected = False connection.register_packet_listener(disconnect, clientbound.play.DisconnectPacket) connection.connect() while connected: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection(options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.profile.name) connection = Connection(options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it # unless explicitly requested by the user. if options.dump_unknown: print('--> [unknown packet] %s' % packet, file=sys.stderr) else: print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener(print_incoming, Packet, early=True) connection.register_packet_listener(print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener(handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet): js = ujson.loads(chat_packet.json_data) if len(js) == 1 and js.get('text'): js['translate'] = "MCDR" js['with'] = [0, 0, 0] try: translate = js['translate'] msg = js['with'][-1] if type(msg) is dict: msg = msg['text'] # 1.15.2 server message = '[{} {}] '.format( datetime.now().strftime("%Y-%m-%d %H:%M:%S"), chat_packet.field_string('position')) try: name = js['with'][0]['insertion'] except: name = None if translate == 'chat.type.announcement': # from server message += '[Server] {}'.format(msg) elif translate == 'chat.type.text': # chat message += '<{}> {}'.format(name, msg) try: uuid = js['with'][0]['hoverEvent']['contents'][ 'id'] # 1.16 server except: try: text = js['with'][0]['hoverEvent']['value']['text'] except TypeError: # 1.15.2 server text = js['with'][0]['hoverEvent']['value'][0]['text'] uuid = text[text.find(',id:"'):].split('"')[1] elif translate == 'commands.message.display.incoming': # tell message += '<{}>(tell) {}'.format(name, msg['text']) elif translate in [ 'multiplayer.player.joined', 'multiplayer.player.left' ]: # login in/out game message += '{} {} the game'.format(name, translate.split('.')[2]) elif translate == 'chat.type.emote': # me message += '* {} {}'.format(name, msg) elif translate == 'MCDR': message += js.get('text') else: message = chat_packet.json_data print(message) except: print('Cannot resolve chat json data: \n {}'.format( chat_packet.json_data)) pass connection.register_packet_listener(print_chat, clientbound.play.ChatMessagePacket) connection.connect() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()
def main(): options = get_options() if options.offline: print("Connecting in offline mode...") connection = Connection( options.address, options.port, username=options.username) else: auth_token = authentication.AuthenticationToken() try: auth_token.authenticate(options.username, options.password) except YggdrasilError as e: print(e) sys.exit() print("Logged in as %s..." % auth_token.username) connection = Connection( options.address, options.port, auth_token=auth_token) if options.dump_packets: def print_incoming(packet): if type(packet) is Packet: # This is a direct instance of the base Packet type, meaning # that it is a packet of unknown type, so we do not print it # unless explicitly requested by the user. if options.dump_unknown: print('--> [unknown packet] %s' % packet, file=sys.stderr) else: print('--> %s' % packet, file=sys.stderr) def print_outgoing(packet): print('<-- %s' % packet, file=sys.stderr) connection.register_packet_listener( print_incoming, Packet, early=True) connection.register_packet_listener( print_outgoing, Packet, outgoing=True) def handle_join_game(join_game_packet): print('Connected.') connection.register_packet_listener( handle_join_game, clientbound.play.JoinGamePacket) def print_chat(chat_packet): print("Message (%s): %s" % ( chat_packet.field_string('position'), chat_packet.json_data)) connection.register_packet_listener( print_chat, clientbound.play.ChatMessagePacket) def handle_zzz(chat_packet): if json.loads(chat_packet.json_data).get("with", " ")[-1] == "zzz": connection.disconnect() time.sleep(5) connection.connect() connection.register_packet_listener( handle_zzz, clientbound.play.ChatMessagePacket) connection.connect() while True: try: text = input() if text == "/respawn": print("respawning...") packet = serverbound.play.ClientStatusPacket() packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN connection.write_packet(packet) else: packet = serverbound.play.ChatPacket() packet.message = text connection.write_packet(packet) except KeyboardInterrupt: print("Bye!") sys.exit()