Search code examples
pythonwebsocketdiscorddiscord.pywarnings

Warning discord.getaway "discord.gateway: Shard ID None heartbeat blocked for more than 10 seconds."


I tried to write a Discord bot that forwards messages from one server to another. But I get a warning that disconnects my connection to the discord gateway. I read that it is possible because of tyme.slep(), but without it, the connection is always interrupted. As I understand it, DDOS protection is activated due to a large number of requests.

import asyncio
import websocket
import json
from threading import Thread
import discord
import requests
from io import BytesIO
import time
from discord.ext import tasks

# Bot
bot_token = "anything"
user_token = "anything"
client = discord.Client(intents=discord.Intents.default())

# Channels list
f = open('channels.txt', 'r')
channels_file = f.read()
channels_array = channels_file.strip().split('\n')

# Connect func
def send_json_request(ws, request):
    ws.send(json.dumps(request))

def receive_json_response(ws):
    response = ws.recv()
    if response:
        return json.loads(response)

#WebSoket
def on_closed(ws):
    ws.connect("wss://gateway.discord.gg/?v=6&encording=json")

ws = websocket.WebSocket(on_close=on_closed)
ws.connect("wss://gateway.discord.gg/?v=6&encording=json")

def receive_json_response(ws):
    response = ws.recv()
    if response:
        return json.loads(response)

def get_attachment_media(media):
    media_array = []
    for i in media:
        response = requests.get(i['url'])
        im = BytesIO(response.content)
        print(im)
        media_array.append(discord.File(im))
    return media_array

def get_channel(id):
    for i in channels_array:
        if i == id:
            return True
    return False


#Heartbeat
def heartbeat(interval):
    print("Heartbeat begin")

    while True:
        time.sleep(interval)
        heartbeatJSON = {
            "op": 1,
            "d": "null"
        }
        send_json_request(ws, heartbeatJSON)
        print("Heartbeat sent")


@tasks.loop(seconds=0.5)
async def main():
    channel = client.get_channel(anything)
    event = receive_json_response(ws)

    try:
        if event['d']['author']['id'] == 'anything':
            return

        id_channel = event['d']['channel_id']
        id_guild = event['d']['guild_id']

        if  get_channel(id_channel):
            content = event['d']['content']
            attachment_media = get_attachment_media(event['d']['attachments'])

            await channel.send(content, files=attachment_media)

        op_code = event('op')
        if op_code == 11:
            print('Heartbeat recieved')
    except:
        pass


@client.event
async def on_ready():

    event = receive_json_response(ws)

    heartbeat_interval = event['d']['heartbeat_interval'] / 1000

    send_json_request(ws, {
        "op": 2,
        "d": {
            "token": user_token,
            "properties": {
                "$os": 'linux',
                "$browser": 'chrome',
                "$device": 'pc'
            }
        }
    })

    main.start()
    asyncio.run(heartbeat(heartbeat_interval))


client.run(bot_token)

Solution

  • I recommend you to check this answer and adjust it to your code.

    However, if you are just trying to make your bot copy the contenet of the messages sent in one server and sending to another one, you can do it in a easier way using the on_message() event. This is the entire code, which should also prevent any warning (unless the bot tries to send too many messages in a short period of time):

    import discord
    
    intents = discord.Intents.default()
    intents.message_content = True # You are missing the message_content intent! (needed to read the content of the guild's messages)
    client = discord.Client(intents=intents)
    
    TOKEN = "Your Token"
    guild_id = 123456789    # The guild you want your bot to send the messages to
    channel_id = 987654321  # The channel of the guild you want your bot to send the messages to
    guild = None
    channel = None
    
    @client.event
    async def on_ready():
        global guild, channel, guild_id, channel_id
        await client.wait_until_ready()
        guild = client.get_guild(guild_id)
        channel = guild.get_channel(channel_id)
        print("Logged")
    
    @client.event
    async def on_message(message : discord.Message):
        if message.author == client.user:   # You don't want to send the own bot messages
            return
        if message.guild.id == guild_id:    # You don't want to send the messages from the own guild your bot is sending the messages to
            return
        await channel.send(f"{message.author}: {message.content}") # Add attachments if you want
    
    client.run(TOKEN)