Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

469
Views
¿Cómo obtengo el estado móvil para el bot de discord modificando directamente el paquete IDENTIFY?

Aparentemente, los bots de discordia pueden tener un estado móvil en lugar del estado de escritorio (en línea) que se obtiene de forma predeterminada.

bot con estado móvil

Después de investigar un poco, descubrí que dicho estado se logra modificando el IDENTIFY packet en discord.gateway.DiscordWebSocket.identify modificando el valor de $browser a Discord Android o Discord iOS teóricamente debería darnos el estado móvil.

Después de modificar fragmentos de código que encontré en línea que hacen esto, termino con esto:

 def get_mobile(): """ The Gateway's IDENTIFY packet contains a properties field, containing $os, $browser and $device fields. Discord uses that information to know when your phone client and only your phone client has connected to Discord, from there they send the extended presence object. The exact field that is checked is the $browser field. If it's set to Discord Android on desktop, the mobile indicator is is triggered by the desktop client. If it's set to Discord Client on mobile, the mobile indicator is not triggered by the mobile client. The specific values for the $os, $browser, and $device fields are can change from time to time. """ import ast import inspect import re import discord def source(o): s = inspect.getsource(o).split("\n") indent = len(s[0]) - len(s[0].lstrip()) return "\n".join(i[indent:] for i in s) source_ = source(discord.gateway.DiscordWebSocket.identify) patched = re.sub( r'([\'"]\$browser[\'"]:\s?[\'"]).+([\'"])', r"\1Discord Android\2", source_, ) loc = {} exec(compile(ast.parse(patched), "<string>", "exec"), discord.gateway.__dict__, loc) return loc["identify"]

Ahora todo lo que queda por hacer es sobrescribir discord.gateway.DiscordWebSocket.identify durante el tiempo de ejecución en el archivo principal, algo como esto:

 import discord import os from discord.ext import commands import mobile_status discord.gateway.DiscordWebSocket.identify = mobile_status.get_mobile() bot = commands.Bot(command_prefix="?") @bot.event async def on_ready(): print(f"Sucessfully logged in as {bot.user}") bot.run(os.getenv("DISCORD_TOKEN"))

Y obtenemos el estado del móvil con éxito.
estado móvil exitoso para bot

Pero aquí está el problema , quería modificar directamente el archivo (que tenía la función) en lugar de parchearlo durante el tiempo de ejecución. Así que cloné dpy lib localmente y edité el archivo en mi máquina, terminó luciendo así:

 async def identify(self): """Sends the IDENTIFY packet.""" payload = { 'op': self.IDENTIFY, 'd': { 'token': self.token, 'properties': { '$os': sys.platform, '$browser': 'Discord Android', '$device': 'Discord Android', '$referrer': '', '$referring_domain': '' }, 'compress': True, 'large_threshold': 250, 'v': 3 } } # ...

(editado $browser y $device a Discord Android solo para estar seguro)

Pero esto no funciona y solo me da el icono normal del escritorio en línea.
Entonces, lo siguiente que hice fue inspeccionar la función de identify después de que se haya aplicado un parche mono, para poder mirar el código fuente y ver qué salió mal antes, pero debido a la mala suerte, obtuve este error:

 Traceback (most recent call last): File "c:\Users\Achxy\Desktop\fresh\file.py", line 8, in <module> print(inspect.getsource(discord.gateway.DiscordWebSocket.identify)) File "C:\Users\Achxy\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 1024, in getsource lines, lnum = getsourcelines(object) File "C:\Users\Achxy\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 1006, in getsourcelines lines, lnum = findsource(object) File "C:\Users\Achxy\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 835, in findsource raise OSError('could not get source code') OSError: could not get source code

Código:

 import discord import os from discord.ext import commands import mobile_status import inspect discord.gateway.DiscordWebSocket.identify = mobile_status.get_mobile() print(inspect.getsource(discord.gateway.DiscordWebSocket.identify)) bot = commands.Bot(command_prefix="?") @bot.event async def on_ready(): print(f"Sucessfully logged in as {bot.user}") bot.run(os.getenv("DISCORD_TOKEN"))

Dado que este mismo comportamiento se exhibió para cada función parcheada (la mencionada anteriormente y loc["identify"] ), ya no pude usar inspect.getsource(...) y luego confié en dis.dis , lo que condujo a resultados mucho más decepcionantes.

Los datos desensamblados se ven exactamente idénticos a la versión de trabajo con parches de mono, por lo que la versión modificada directamente simplemente no funciona a pesar de que el contenido de la función es exactamente el mismo. (En lo que respecta a los datos desmontados)

Notas: Hacer Discord iOS directamente tampoco funciona, cambiando el $device a algún otro valor pero manteniendo $browser no funciona, he probado todas las combinaciones, ninguna funciona.

TL; DR: ¿Cómo obtener el estado móvil para el bot discord sin parchearlo durante el tiempo de ejecución?

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Lo siguiente funciona subclasificando la clase relevante y duplicando el código con los cambios relevantes. También tenemos que crear una subclase de la clase Client , para sobrescribir el lugar donde se usa la clase gateway/websocket. Esto da como resultado una gran cantidad de código duplicado, sin embargo, funciona y no requiere parches sucios ni edición del código fuente de la biblioteca.

Sin embargo, viene con muchos de los mismos problemas que la edición del código fuente de la biblioteca, principalmente porque a medida que la biblioteca se actualiza, este código quedará obsoleto (si está utilizando la versión archivada y obsoleta de la biblioteca, tiene problemas más grandes en su lugar).

 import asyncio import sys import aiohttp import discord from discord.gateway import DiscordWebSocket, _log from discord.ext.commands import Bot class MyGateway(DiscordWebSocket): async def identify(self): payload = { 'op': self.IDENTIFY, 'd': { 'token': self.token, 'properties': { '$os': sys.platform, '$browser': 'Discord Android', '$device': 'Discord Android', '$referrer': '', '$referring_domain': '' }, 'compress': True, 'large_threshold': 250, 'v': 3 } } if self.shard_id is not None and self.shard_count is not None: payload['d']['shard'] = [self.shard_id, self.shard_count] state = self._connection if state._activity is not None or state._status is not None: payload['d']['presence'] = { 'status': state._status, 'game': state._activity, 'since': 0, 'afk': False } if state._intents is not None: payload['d']['intents'] = state._intents.value await self.call_hooks('before_identify', self.shard_id, initial=self._initial_identify) await self.send_as_json(payload) _log.info('Shard ID %s has sent the IDENTIFY payload.', self.shard_id) class MyBot(Bot): async def connect(self, *, reconnect: bool = True) -> None: """|coro| Creates a websocket connection and lets the websocket listen to messages from Discord. This is a loop that runs the entire event system and miscellaneous aspects of the library. Control is not resumed until the WebSocket connection is terminated. Parameters ----------- reconnect: :class:`bool` If we should attempt reconnecting, either due to internet failure or a specific failure on Discord's part. Certain disconnects that lead to bad state will not be handled (such as invalid sharding payloads or bad tokens). Raises ------- :exc:`.GatewayNotFound` If the gateway to connect to Discord is not found. Usually if this is thrown then there is a Discord API outage. :exc:`.ConnectionClosed` The websocket connection has been terminated. """ backoff = discord.client.ExponentialBackoff() ws_params = { 'initial': True, 'shard_id': self.shard_id, } while not self.is_closed(): try: coro = MyGateway.from_client(self, **ws_params) self.ws = await asyncio.wait_for(coro, timeout=60.0) ws_params['initial'] = False while True: await self.ws.poll_event() except discord.client.ReconnectWebSocket as e: _log.info('Got a request to %s the websocket.', e.op) self.dispatch('disconnect') ws_params.update(sequence=self.ws.sequence, resume=e.resume, session=self.ws.session_id) continue except (OSError, discord.HTTPException, discord.GatewayNotFound, discord.ConnectionClosed, aiohttp.ClientError, asyncio.TimeoutError) as exc: self.dispatch('disconnect') if not reconnect: await self.close() if isinstance(exc, discord.ConnectionClosed) and exc.code == 1000: # clean close, don't re-raise this return raise if self.is_closed(): return # If we get connection reset by peer then try to RESUME if isinstance(exc, OSError) and exc.errno in (54, 10054): ws_params.update(sequence=self.ws.sequence, initial=False, resume=True, session=self.ws.session_id) continue # We should only get this when an unhandled close code happens, # such as a clean disconnect (1000) or a bad state (bad token, no sharding, etc) # sometimes, discord sends us 1000 for unknown reasons so we should reconnect # regardless and rely on is_closed instead if isinstance(exc, discord.ConnectionClosed): if exc.code == 4014: raise discord.PrivilegedIntentsRequired(exc.shard_id) from None if exc.code != 1000: await self.close() raise retry = backoff.delay() _log.exception("Attempting a reconnect in %.2fs", retry) await asyncio.sleep(retry) # Always try to RESUME the connection # If the connection is not RESUME-able then the gateway will invalidate the session. # This is apparently what the official Discord client does. ws_params.update(sequence=self.ws.sequence, resume=True, session=self.ws.session_id) bot = MyBot(command_prefix="?") @bot.event async def on_ready(): print(f"Sucessfully logged in as {bot.user}") bot.run("YOUR_BOT_TOKEN")

Personalmente, creo que el siguiente enfoque, que incluye algunos parches mono en tiempo de ejecución (pero no manipulación AST) es más limpio para este propósito:

 import sys from discord.gateway import DiscordWebSocket, _log from discord.ext.commands import Bot async def identify(self): payload = { 'op': self.IDENTIFY, 'd': { 'token': self.token, 'properties': { '$os': sys.platform, '$browser': 'Discord Android', '$device': 'Discord Android', '$referrer': '', '$referring_domain': '' }, 'compress': True, 'large_threshold': 250, 'v': 3 } } if self.shard_id is not None and self.shard_count is not None: payload['d']['shard'] = [self.shard_id, self.shard_count] state = self._connection if state._activity is not None or state._status is not None: payload['d']['presence'] = { 'status': state._status, 'game': state._activity, 'since': 0, 'afk': False } if state._intents is not None: payload['d']['intents'] = state._intents.value await self.call_hooks('before_identify', self.shard_id, initial=self._initial_identify) await self.send_as_json(payload) _log.info('Shard ID %s has sent the IDENTIFY payload.', self.shard_id) DiscordWebSocket.identify = identify bot = Bot(command_prefix="?") @bot.event async def on_ready(): print(f"Sucessfully logged in as {bot.user}") bot.run("YOUR_DISCORD_TOKEN")

En cuanto a por qué la edición del código fuente de la biblioteca no funcionó para usted, solo puedo suponer que ha editado la copia incorrecta del archivo, como ha comentado la gente.

over 4 years ago · Santiago Trujillo Report

0

DiscordWebSocket.identify no es trivial y no existe una forma admitida de anular esos campos.

Una alternativa más fácil de mantener que copiar y pegar 35* líneas de código para modificar 2 líneas es crear una subclase y luego anular DiscordWebSocket.send_as_json (4 líneas de código personalizado) y parchear el método de DiscordWebSocket.from_client classmethod instanciar la subclase:

 import os from discord.ext import commands from discord.gateway import DiscordWebSocket class MyDiscordWebSocket(DiscordWebSocket): async def send_as_json(self, data): if data.get('op') == self.IDENTIFY: if data.get('d', {}).get('properties', {}).get('$browser') is not None: data['d']['properties']['$browser'] = 'Discord Android' data['d']['properties']['$device'] = 'Discord Android' await super().send_as_json(data) DiscordWebSocket.from_client = MyDiscordWebSocket.from_client bot = commands.Bot(command_prefix="?") @bot.event async def on_ready(): print(f"Sucessfully logged in as {bot.user}") bot.run(os.getenv("DISCORD_TOKEN"))

*39 líneas en Pycord 1.7.3. Al anular, obtiene actualizaciones futuras generalmente sin esfuerzo adicional.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!