예제 #1
0
파일: client.py 프로젝트: Mikeses/aiotfm
	def __init__(self, community=Community.en, auto_restart=False, loop=None):
		self.loop = loop or asyncio.get_event_loop()

		self.main = Connection('main', self, self.loop)
		self.bulle = None

		self._waiters = {}

		self.room = None
		self.trade = None
		self.trades = {}
		self.inventory = None

		self.username = None
		self.locale = Locale()
		self.community = Community(community)
		self.cp_fingerprint = 0

		self.keys = None
		self.authkey = 0

		self.auto_restart = auto_restart
		self.api_tfmid = None
		self.api_token = None

		self._channels = []
예제 #2
0
파일: bot.py 프로젝트: jpnspank/kilin
async def commu(ctx, commu):
    if str(ctx.author) in client_admins:
        commu = str(commu)

        try:
            try:
                client.community = Community(int(commu))
            except ValueError:
                client.community = Community[commu]
        except KeyError:
            client.community = Community.br

        loop.create_task(client.restart())
예제 #3
0
파일: client.py 프로젝트: Mikeses/aiotfm
	async def joinRoom(self, room_name, password=None, community=None, auto=False):
		"""|coro|
		Join a room.
		The event 'on_joined_room' is dispatched when the client has successfully joined the room.

		:param password: :class:`str` if given the client will ignore `community` and `auto` parameters
			and will connect to the room with the given password.
		:param room_name: :class:`str` the room's name.
		:param community: Optional[:class:`int`] the room's community.
		:param auto: Optional[:class:`bool`] joins a random room (I think).
		"""
		if password is not None:
			packet = Packet.new(5, 39).writeString(password).writeString(room_name)
		else:
			packet = Packet.new(5, 38).write8(Community(community or self.community).value)
			packet.writeString(room_name).writeBool(auto)

		await self.main.send(packet)
예제 #4
0
파일: bot.py 프로젝트: jpnspank/kilin
    async def on_whisper(self, _message):
        author = str(_message.author)
        commu = ChatCommunity(_message.community).name
        message = _message.content
        args = message.split()

        print(f"[Whisper][{commu}][{author}] {message}")

        # Whispers commands
        if author in client_admins:
            level = client_admins[author]
            # Level 0 Admins only
            if level == 0:
                if args[0] == "!ban":
                    if len(args) > 1:
                        await self.sendCommand('ban ' + args[1])
                    return
                elif args[0] == "!target":
                    if len(args) > 1:
                        self.elim_target = args[1]
                    return
                elif args[0] == '!press6':
                    await self.sendSmiley(5)
                    return
                elif args[0] == '!press3':
                    await self.sendSmiley(2)                
                    return
                elif args[0] == '!dance':
                    await self.playEmote(0)
                    return
                elif args[0] == "!callback":
                    self.can_press_callback = not self.can_press_callback
                    return
                elif args[0] == "!room":
                    if len(args) > 1:
                        room = ' '.join(args[1:])
                        return await self.joinRoom(room)
                    return await _message.reply(self._room.name)
                elif args[0] == "!pwroom":
                    if len(args) == 1:
                        return
                    return await self.joinRoom(" ".join(args[2:]), args[1])
                elif args[0] == "!canplay":
                    self.can_play, self.is_playing = not self.can_play, False
                    return self.cancel_mov_task()
                    return await self._mort()
                elif args[0] == "!candie":
                    self.can_die = not self.can_die
                    return
                elif args[0] == "!cheese":
                    return await self._cheese()
                elif args[0] == "!win":
                    return await self._win()
                elif args[0] == "!del":
                    code = str(self._room.map.code)
                    if len(args) > 1:
                        code = args[1]
                    return await self.del_map(author, code)
                elif args[0] == "!inv":
                    self.can_accept_inv = not self.can_accept_inv
                    return

            # Level 0+ Admins
            if level >= 0:
                if args[0] == "!lua":
                    if len(args) > 1:
                        if "pastebin" not in args[1]:
                            return await _message.reply("[Error] Code must be hosted on Pastebin")
                        await self.run_code(args[1])
                    return
                elif args[0] in ["!come", "!follow", "!seg"]:
                    target = author
                    if len(args) > 1:
                        target = args[1]

                    tribe = await self.getTribe(disconnected=False)
                    if tribe is not None:
                        member = tribe.get_member(target)
                        if member is not None and member.room is not None:
                            return await self.joinRoom(member.room)
                    if self.friends is not None:
                        friend = self.friends.get_friend(target)
                        if friend is not None and friend.room is not None:
                            return await self.joinRoom(friend.room)
                    return await _message.reply("Player not found")
                elif args[0] == "!commu":
                    commu = 1
                    if len(args) > 1:
                        try:
                            commu = Community(int(args[1]))
                        except:
                            commu = Community[args[1]]
                    self.community = commu
                    return loop.create_task(self.restart())
                elif args[0] == "!say":
                    return await self.sendRoomMessage(" ".join(args[1:]))
                elif args[0] in ["!th", "!house"]:
                    return await self.enterTribe()
                elif args[0] == "!lua":
                    if len(args) == 1:
                        return
                    return await self.run_code(args[1])
                elif args[0] == "!pm":
                    if len(args) == 1:
                        return
                    return await self.whisper(args[1], " ".join(args[2:]))
                elif args[0] == "!tm":
                    if len(args) == 1:
                        return
                    return await self.sendTribeMessage(" ".join(args[1:]))
                elif args[0] == "!mort":
                    return await self._mort()
예제 #5
0
파일: client.py 프로젝트: Mikeses/aiotfm
	async def handle_packet(self, connection: Connection, packet: Packet):
		"""|coro|
		Handles the known packets and dispatches events.
		Subclasses should handle only the unhandled packets from this method.

		Example: ::
			class Bot(aiotfm.Client):
				async def handle_packet(self, conn, packet):
					handled = await super().handle_packet(conn, packet.copy())

					if not handled:
						# Handle here the unhandled packets.
						pass

		:param connection: :class:`aiotfm.Connection` the connection that received
			the packet.
		:param packet: :class:`aiotfm.Packet` the packet.
		:return: True if the packet got handled, False otherwise.
		"""
		CCC = packet.readCode()
		if CCC == (1, 1): # Old packets
			oldCCC, *data = packet.readString().split(b'\x01')
			data = list(map(bytes.decode, data))
			oldCCC = tuple(oldCCC[:2])

			# :desc: Called when an old packet is received. Does not interfere
			# with :meth:`Client.handle_old_packet`.
			# :param connection: :class:`aiotfm.Connection` the connection that received
			# the packet.
			# :param oldCCC: :class:`tuple` the packet identifiers on the old protocol.
			# :param data: :class:`list` the packet data.
			self.dispatch('old_packet', connection, oldCCC, data)
			return await self.handle_old_packet(connection, oldCCC, data)

		if CCC == (5, 21): # Joined room
			self.room = Room(official=packet.readBool(), name=packet.readUTF())

			# :desc: Called when the client has joined a room.
			# :param room: :class:`aiotfm.room.Room` the room the client has entered.
			self.dispatch('joined_room', self.room)

		elif CCC == (5, 39): # Password required for the room

			# :desc: Called when a password is required to enter a room
			# :param room: :class:`aiotfm.room.Room` the room the server is asking for a password.
			self.dispatch('room_password', Room(packet.readUTF()))

		elif CCC == (6, 6): # Room message
			player_id = packet.read32()
			username = packet.readUTF()
			commu = packet.read8()
			message = packet.readUTF()
			player = self.room.get_player(pid=player_id)

			if player is None:
				player = Player(username, pid=player_id)

			# :desc: Called when the client receives a message from the room.
			# :param message: :class:`aiotfm.message.Message` the message.
			self.dispatch('room_message', Message(player, message, commu, self))

		elif CCC == (6, 20): # Server message
			packet.readBool() # if False then the message will appear in the #Server channel
			t_key = packet.readUTF()
			t_args = [packet.readUTF() for i in range(packet.read8())]

			# :desc: Called when the client receives a message from the server that needs to be translated.
			# :param message: :class:`aiotfm.locale.Translation` the message translated with the
			# current locale.
			# :param *args: a list of string used as replacement inside the message.
			self.dispatch('server_message', self.locale[t_key], *t_args)

		elif CCC == (8, 5): # Show emoji
			player = self.room.get_player(pid=packet.read32())
			emoji = packet.read8()

			# :desc: Called a player is showing an emoji above its head.
			# :param player: :class:`aiotfm.Player` the player.
			# :param emoji: :class:`int` the emoji's id.
			self.dispatch('emoji', player, emoji)

		elif CCC == (8, 6): # Player won
			packet.read8()
			player = self.room.get_player(pid=packet.read32())
			player.score = packet.read16()
			order = packet.read8()
			player_time = packet.read16() / 100

			# :desc: Called a player get the cheese to the hole.
			# :param player: :class:`aiotfm.Player` the player.
			# :param order: :class:`int` the order of the player in the hole.
			# :param player_time: :class:`float` player's time in the hole in seconds.
			self.dispatch('player_won', player, order, player_time)

		elif CCC == (8, 16): # Profile
			# :desc: Called when the client receives the result of a /profile command.
			# :param profile: :class:`aiotfm.player.Profile` the profile.
			self.dispatch('profile', Profile(packet))

		elif CCC == (8, 20): # Shop
			# :desc: Called when the client receives the content of the shop.
			# :param shop: :class:`aiotfm.shop.Shop` the shop.
			self.dispatch('shop', Shop(packet))

		elif CCC == (8, 22): # Skills
			skills = {}
			for _ in range(packet.read8()):
				key, value = packet.read8(), packet.read8()
				skills[key] = value

			# :desc: Called when the client receives its skill tree.
			# :param skills: :class:`dict` the skills.
			self.dispatch('skills', skills)

		elif CCC == (16, 2): # Tribe invitation received
			author = packet.readUTF()
			tribe = packet.readUTF()

			# :desc: Called when the client receives an invitation to a tribe. (/inv)
			# :param author: :class:`str` the player that invited you.
			# :param tribe: :class:`str` the tribe.
			self.dispatch('tribe_inv', author, tribe)

		elif CCC == (26, 2): # Logged in successfully
			player_id = packet.read32()
			self.username = username = packet.readUTF()
			played_time = packet.read32()
			community = Community(packet.read8())
			pid = packet.read32()

			# :desc: Called when the client successfully logged in.
			# :param uid: :class:`int` the client's unique id.
			# :param username: :class:`str` the client's username.
			# :param played_time: :class:`int` the total number of minutes the client has played.
			# :param community: :class:`aiotfm.enums.Community` the community the client has connected to.
			# :param pid: :class:`int` the client's player id.
			self.dispatch('logged', player_id, username, played_time, community, pid)

		elif CCC == (26, 3): # Handshake OK
			online_players = packet.read32()
			connection.fingerprint = packet.read8()
			community = Community[packet.readUTF()]
			country = packet.readUTF()
			self.authkey = packet.read32()

			self.loop.create_task(self._heartbeat_loop())

			await connection.send(Packet.new(8, 2).write8(self.community.value).write8(0))

			os_info = Packet.new(28, 17).writeString('en').writeString('Linux')
			os_info.writeString('LNX 29,0,0,140').write8(0)

			await connection.send(os_info)

			# :desc: Called when the client can login through the game.
			# :param online_players: :class:`int` the number of player connected to the game.
			# :param community: :class:`aiotfm.enums.Community` the community the server suggest.
			# :param country: :class:`str` the country detected from your ip.
			self.dispatch('login_ready', online_players, community, country)

		elif CCC == (26, 12): # Login result
			# :desc: Called when the client failed logging.
			# :param code: :class:`int` the error code.
			# :param code: :class:`str` error messages.
			# :param code: :class:`str` error messages.
			self.dispatch('login_result', packet.read8(), packet.readUTF(), packet.readUTF())

		elif CCC == (26, 25): # Ping
			# :desc: Called when the client receives the ping response from the server.
			self.dispatch('ping')

		elif CCC == (28, 6): # Server ping
			await connection.send(Packet.new(28, 6).write8(packet.read8()))

		elif CCC == (29, 6): # Lua logs
			# :desc: Called when the client receives lua logs from #Lua.
			# :param log: :class:`str` a log message.
			self.dispatch('lua_log', packet.readUTF())

		elif CCC == (31, 1): # Inventory data
			self.inventory = Inventory.from_packet(packet)
			self.inventory.client = self

			# :desc: Called when the client receives its inventory's content.
			# :param inventory: :class:`aiotfm.inventory.Inventory` the client's inventory.
			self.dispatch('inventory_update', self.inventory)

		elif CCC == (31, 2): # Update inventory item
			item_id = packet.read16()
			quantity = packet.read8()

			if item_id in self.inventory.items:
				item = self.inventory.items[item_id]
				previous = item.quantity
				item.quantity = quantity

				# :desc: Called when the quantity of an item has been updated.
				# :param item: :class:`aiotfm.inventory.InventoryItem` the new item.
				# :param previous: :class:`aiotfm.inventory.InventoryItem` the previous item.
				self.dispatch('item_update', item, previous)

			else:
				item = InventoryItem(item_id=item_id, quantity=quantity)
				self.inventory.items[item.id] = item

				# :desc: Called when the client receives a new item in its inventory.
				# :param item: :class:`aiotfm.inventory.InventoryItem` the new item.
				self.dispatch('new_item', item)

		elif CCC == (31, 5): # Trade invite
			pid = packet.read32()

			self.trades[pid] = Trade(self, self.room.get_player(pid=pid))

			# :desc: Called when received an invitation to trade.
			# :param trade: :class:`aiotfm.inventory.Trade` the trade object.
			self.dispatch('trade_invite', self.trades[pid])

		elif CCC == (31, 6): # Trade error
			name = packet.readUTF().lower()
			error = packet.read8()

			if name == self.username.lower():
				trade = self.trade
			else:
				for t in self.trades.values():
					if t.trader.lower() == name:
						trade = t
						break

			# :desc: Called when an error occurred with a trade.
			# :param trade: :class:`aiotfm.inventory.Trade` the trade that failed.
			# :param error: :class:`aiotfm.enums.TradeError` the error.
			self.dispatch('trade_error', trade, TradeError[error])
			trade._close()

		elif CCC == (31, 7): # Trade start
			pid = packet.read32()
			trade = self.trades.get(pid)

			if trade is None:
				raise AiotfmException(f'Cannot find the trade from pid {pid}.')

			trade._start()
			self.trade = trade

			# :desc: Called when a trade starts. You can access the trade object with `Client.trade`.
			self.dispatch('trade_start')

		elif CCC == (31, 8): # Trade items
			export = packet.readBool()
			id_ = packet.read16()
			quantity = (1 if packet.readBool() else -1) * packet.read8()

			items = self.trade.exports if export else self.trade.imports
			items.add(id_, quantity)

			trader = self if export else self.trade.trader
			self.trade.locked = [False, False]

			# :desc: Called when an item has been added/removed from the current trade.
			# :param trader: :class:`aiotfm.Player` the player that triggered the event.
			# :param id: :class:`int` the item's id.
			# :param quantity: :class:`int` the quantity added/removed. Can be negative.
			# :param item: :class:`aiotfm.inventory.InventoryItem` the item after the change.
			self.dispatch('trade_item_change', trader, id_, quantity, items.get(id_))

		elif CCC == (31, 9): # Trade lock
			index = packet.read8()
			locked = packet.readBool()
			if index > 1:
				self.trade.locked = [locked, locked]
				who = "both"
			else:
				self.trade.locked[index] = locked
				who = self.trade.trader if index == 0 else self

			# :desc: Called when the trade got (un)locked.
			# :param who: :class:`aiotfm.Player` the player that triggered the event.
			# :param locked: :class:`bool` either the trade got locked or unlocked.
			self.dispatch('trade_lock', who, locked)

		elif CCC == (31, 10): # Trade complete
			trade = self.trade
			self.trade._close(succeed=True)

		elif CCC == (44, 1): # Bulle switching
			timestamp = packet.read32()
			player_id = packet.read32()
			bulle_ip = packet.readUTF()
			ports = packet.readUTF().split('-')

			if self.bulle is not None:
				self.bulle.close()

			self.bulle = Connection('bulle', self, self.loop)
			await self.bulle.connect(bulle_ip, random.choice(ports))
			await self.bulle.send(Packet.new(*CCC).write32(timestamp).write32(player_id))

		elif CCC == (44, 22): # Fingerprint offset changed
			connection.fingerprint = packet.read8()

		elif CCC == (60, 3): # Community platform
			TC = packet.read16()

			# :desc: Called when the client receives a packet from the community platform.
			# :param TC: :class:`int` the packet's code.
			# :param packet: :class:`aiotfm.Packet` the packet.
			self.dispatch('raw_cp', TC, packet.copy(copy_pos=True))

			if TC == 3: # Connected to the community platform
				# :desc: Called when the client is successfully connected to the community platform.
				self.dispatch('ready')

			elif TC == 55: # Channel join result
				result = packet.read8()

				# :desc: Called when the client receives the result of joining a channel.
				# :param result: :class:`int` result code.
				self.dispatch('channel_joined_result', result)

			elif TC == 57: # Channel leave result
				result = packet.read8()

				# :desc: Called when the client receives the result of leaving a channel.
				# :param result: :class:`int` result code.
				self.dispatch('channel_leaved_result', result)

			elif TC == 59: # Channel /who result
				idSequence = packet.read32()
				result = packet.read8()
				players = [Player(packet.readUTF()) for _ in range(packet.read16())]

				# :desc: Called when the client receives the result of the /who command in a channel.
				# :param idSequence: :class:`int` the reference to the packet that performed the request.
				# :param players: List[:class:`aiotfm.Player`] the list of players inside the channel.
				self.dispatch('channel_who', idSequence, players)

			elif TC == 62: # Joined a channel
				name = packet.readUTF()

				if name in self._channels:
					channel = [c for c in self._channels if c == name][0]
				else:
					channel = Channel(name, self)
					self._channels.append(channel)

				# :desc: Called when the client joined a channel.
				# :param channel: :class:`aiotfm.message.Channel` the channel.
				self.dispatch('channel_joined', channel)

			elif TC == 63: # Quit a channel
				name = packet.readUTF()
				if name in self._channels:
					self._channels.remove(name)

				# :desc: Called when the client leaves a channel.
				# :param name: :class:`str` the channel's name.
				self.dispatch('channel_closed', name)

			elif TC == 64: # Channel message
				author, community = packet.readUTF(), packet.read32()
				channel_name, message = packet.readUTF(), packet.readUTF()
				channel = self.get_channel(channel_name)

				if channel is None:
					channel = Channel(channel_name, self)
					self._channels.append(channel)

				channel_message = ChannelMessage(author, community, message, channel)

				# :desc: Called when the client receives a message from a channel.
				# :param message: :class:`aiotfm.message.ChannelMessage` the message.
				self.dispatch('channel_message', channel_message)

			elif TC == 65: # Tribe message
				author, message = packet.readUTF(), packet.readUTF()

				# :desc: Called when the client receives a message from the tribe.
				# :param author: :class:`str` the message's author.
				# :param message: :class:`str` the message's content.
				self.dispatch('tribe_message', author, message)

			elif TC == 66: # Whisper
				author = Player(packet.readUTF())
				commu = packet.read32()
				receiver = Player(packet.readUTF())
				message = packet.readUTF()

				author = self.room.get_player(name=author, default=author)
				receiver = self.room.get_player(name=receiver, default=receiver)

				# :desc: Called when the client receives a whisper.
				# :param message: :class:`aiotfm.message.Whisper` the message.
				self.dispatch('whisper', Whisper(author, commu, receiver, message, self))

			elif TC == 88: # tribe member connected
				# :desc: Called when a tribe member connected.
				# :param name: :class:`str` the member's name.
				self.dispatch('member_connected', packet.readUTF())

			elif TC == 90: # tribe member disconnected
				# :desc: Called when a tribe member disconnected.
				# :param name: :class:`str` the member's name.
				self.dispatch('member_disconnected', packet.readUTF())

			else:
				if self.LOG_UNHANDLED_PACKETS:
					print(CCC, TC, bytes(packet.buffer)[4:])
				return False

		elif CCC == (100, 67): # New inventory item
			slot = packet.read8()
			slot = None if slot == 0 else slot
			item_id = packet.read16()
			quantity = packet.read8()

			item = InventoryItem(item_id=item_id, quantity=quantity, slot=slot)
			self.inventory[item_id] = item
			self.dispatch('new_item', item)

		elif CCC == (144, 1): # Set player list
			before = self.room.players
			self.room.players = {}

			for _ in range(packet.read16()):
				player = Player.from_packet(packet)
				self.room.players[player.pid] = player

			# :desc: Called when the client receives an update of all player in the room.
			# :param before: Dict[:class:`aiotfm.Player`] the list of player before the update.
			# :param players: Dict[:class:`aiotfm.Player`] the list of player updated.
			self.dispatch('bulk_player_update', before, self.room.players)

		elif CCC == (144, 2): # Add a player
			after = Player.from_packet(packet)
			before = self.room.players.pop(after.pid, None)

			self.room.players[after.pid] = after
			if before is None:
				# :desc: Called when a player joined the room.
				# :param player: :class:`aiotfm.Player` the player.
				self.dispatch('player_join', after)
			else:
				# :desc: Called when a player's data on the room has been updated.
				# :param before: :class:`aiotfm.Player` the player before the update.
				# :param player: :class:`aiotfm.Player` the player updated.
				self.dispatch('player_update', before, after)

		else:
			if self.LOG_UNHANDLED_PACKETS:
				print(CCC, bytes(packet.buffer)[2:])
			return False

		return True