Discord.py - Sending to multiple channels - python-3.x

I have a working bot that sends posts from my discord server to another. I'm looking for a way to add additional channels of other servers but can't figure out how to get it to work. I read somewhere that get_channel only works once and that I need to incorporate it into a loop? I'm new to python and discord.py so most likely I just don't understand. Here's my code, Hopefully, someone can help.
import discord
intents = discord.Intents(messages=True, guilds=True)
from discord.ext import commands
test001from=1029028218684055562
test001to=1028777554217291806, 1028777583808098304
bot = commands.Bot(command_prefix = ".");
#bot.event
async def on_ready():
print("Bot Active")
#bot.event
async def on_message(message):
if message.channel.id == test001from:
channeltosend = bot.get_channel(test001to)
await channeltosend.send(message.content)
bot.run("TOKEN")

First, put all the channel ids into a list like so: test001to[1028777554217291806, 1028777583808098304]
Second, use get_channel in the on_ready function and save them as a new list.
Third, use a for loop to iterate through the new list you created for chan in channels and send the message to each channel chan.send(message.content).

Related

Bot not responding discord.py

Here is my code
import discord
from discord.ext import commands
TOKEN = "MY TOKEN"
import random
intent = discord.Intents.default()
intent.members = True
intent.message_content = True
client = discord.Client(intents=intent)
bot = commands.Bot(command_prefix='--', intents=discord.Intents.default())
slap_gif = ['https://media.tenor.com/GBShVmDnx9kAAAAC/anime-slap.gif', 'https://media.tenor.com/CvBTA0GyrogAAAAC/anime-slap.gif', 'https://i.pinimg.com/originals/fe/39/cf/fe39cfc3be04e3cbd7ffdcabb2e1837b.gif', 'https://i.pinimg.com/originals/68/de/67/68de679cc20000570e8a7d9ed9218cd3.gif']
slap_txt = ['Ya, you deserve it', 'Get slapped', 'Ya, get slapped hard']
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#bot.command()
async def slap(ctx):
embed = discord.Embed(
color=discord.Color("#ee5253"),
description = f"{ctx.author.mention} {(random.choice(slap_txt))}"
)
embed.set_image(url=(random.choice(slap_gif)))
await ctx.send(embed=embed)
client.run(TOKEN)
I am trying to create a basic gif action bot but it is not working, it was working with on_message but i switched to #bot.command and it is not working now. it runs with no error.
on_message reacts on any text you send.
#bot.command() just reacts to the the command name with the prefix, which would be in your case --slap.
I just can guess, but eventually you didn't send the correct message text.
Because discord.Bot is a subclass of discord.Client you only need exactly one of the two (because you register the command bot, but you run client, the command registered on bot won't work. Also preferably you should keep the discord.Bot instance which is named bot because it as it is a subclass it has more functionality) and you should replace all occurrences of client with that.
Also your bot seems to have the wrong intents. If I change bot = commands.Bot(..., intents=discord.Intents.default()) to bot = commands.Bot(..., intents=intent) it ran the slap function (It has thrown an error for me, but that's not the topic of this question).

Simplest way to check the users in channel with discord.py

I am making a simple bot, all I want it to do is wait for me to type a command with an argument (a vc), so for example when I type !channel general, the bot will return a list of the members in that channel. So if Bob and Jeff are in General the bot will return member_list = ['Bob', 'Jeff'] Any easy way to do that?
Update:
import discord
import os
from discord.ext import commands
client = discord.Client()
bot = commands.Bot(command_prefix='$')
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#bot.command()
async def members(ctx, channel: discord.VoiceChannel):
member_list = [i.name for i in channel.members]
print(member_list)
await ctx.send(member_list) # or return member_list whatever you want
client.run(os.getenv('TOKEN'))
Here's my code up above, when I run bot it does not do anything when I type $members general, anyone know what I'm doing wrong?
Use VoiceChannel.members
#bot.command()
async def members(ctx, channel: discord.VoiceChannel):
member_list = [i.name for i in channel.members]
await ctx.send(member_list) # or return member_list whatever you want

Add a reaction to a ctx.send message in discord.py

I am making a poll command, the bot will send a ctx message and will say the poll question. I want to make it so when the poll message is sent, the bot adds two reaction, a thumbs up and a thumbs down. I have tried several different ways but non of them work. Here is the code from my latest try (everything is already imported)
reactions = ["👍", "👎"]
#bot.command(pass_context=True)
async def poll(self, ctx, message,*, question):
poll_msg = f"Poll: {question} -{ctx.author}"
reply = await self.bot.say(poll_msg)
for emoji_id in reactions:
emoji = get(ctx.server.emojis, name=emoji_id)
await message.add_reaction(reply, emoji or emoji_id)
The code is all over the place because I tried putting different solutions together to see if it would work but it doesn't work at all.
It looks like you're operating from some old examples. You should read the official documentation to find examples of the modern interfaces.
from discord.ext import commands
from discord.utils import get
bot = commands.Bot("!")
reactions = ["👍", "👎"]
#bot.command()
async def poll(ctx, *, question):
m = await ctx.send(f"Poll: {question} -{ctx.author}")
for name in reactions:
emoji = get(ctx.guild.emojis, name=name)
await m.add_reaction(emoji or name)

How to send a message with discord.py from outside the event loop (i.e. from python-telegram-bot thread)?

I want to use make a bot that communicates between discord and telegram by using the libraries python-telegram-bot and discord.py (version 1.0.0). However the problem is that discord.py uses async functions and python-telegram-bot threading. With the code below, everything works fine for messages being posted in discord (the bot sends them correctly to telegram), however the other way around does not work (bot gets messages from telegram and sends it to discord). I previously had issues with syntax/runtime errors as I tried to run the discords channel.send function in a sync function (thus either returning only a generator object or complaining that I cannot use await in a sync function). However, at the same time the python-telegram-bot's MessageHandler needs a sync function so when giving him a async function Python complains that "await" was never called for the async function.
I now tried to use the async_to_sync method from asgiref library to run my async broadcastMsg from the MessageHandler, however the code still does not send the message to discord! It seems to call the function correctly but only until line print('I get to here'). No error is displayed and no message is poping up in discord. I guess it has something to do with the fact that I have to register the function as a task in the discord.py event loop, however registering is only working when it happens before botDiscord.run(TOKENDISCORD) has been executed which of course has to happen before.
So to boil my problem down to one question:
How am I able to interact with the discord.py event loop from another thread (which is from the telegram MessageHandler). Or if this is not possible: How can I send a message with discord.py without being within the discord.py event loop?
Thank you for your help
import asyncio
from asgiref.sync import async_to_sync
from telegram import Message as TMessage
from telegram.ext import (Updater,Filters,MessageHandler)
from discord.ext import commands
import discord
TChannelID = 'someTelegramChannelID'
DChannel = 'someDiscordChannelObject'
#%% define functions / commands
prefix = "?"
botDiscord = commands.Bot(command_prefix=prefix)
discordChannels = {}
async def broadcastMsg(medium,channel,message):
'''
Function to broadcast a message to all linked channels.
'''
if isinstance(message,TMessage):
fromMedium = 'Telegram'
author = message.from_user.username
channel = message.chat.title
content = message.text
elif isinstance(message,discord.Message):
fromMedium = 'Discord'
author = message.author
channel = message.channel.name
content = message.content
# check where message comes from
textToSend = '%s wrote on %s %s:\n%s'%(author,fromMedium,channel,content)
# go through channels and send the message
if 'telegram' in medium:
# transform channel to telegram chatID and send
updaterTelegram.bot.send_message(channel,textToSend)
elif 'discord' in medium:
print('I get to here')
await channel.send(textToSend)
print("I do not get there")
#botDiscord.event
async def on_message(message):
await broadcastMsg('telegram',TChannelID,message)
def on_TMessage(bot,update):
# check if this chat is already known, else save it
# get channels to send to and send message
async_to_sync(broadcastMsg)('discord',DChannel,update.message)
#%% initialize telegram and discord bot and run them
messageHandler = MessageHandler(Filters.text, on_TMessage)
updaterTelegram = Updater(token = TOKENTELEGRAM, request_kwargs={'read_timeout': 10, 'connect_timeout': 10})
updaterTelegram.dispatcher.add_handler(messageHandler)
updaterTelegram.start_polling()
botDiscord.run(TOKENDISCORD)
How can I send a message with discord.py without being within the discord.py event loop?
To safely schedule a coroutine from outside the event loop thread, use asyncio.run_coroutine_threadsafe:
_loop = asyncio.get_event_loop()
def on_TMessage(bot, update):
asyncio.run_coroutine_threadsafe(
broadcastMsg('discord', DChannel, update.message), _loop)
you can try split them to 2 separates *.py files.
t2d.py #telegram to discor
import subprocess
from telethon import TelegramClient, events, sync
api_id = '...'
api_hash = '...'
with TelegramClient('name', api_id, api_hash) as client:
#client.on(events.NewMessage()) #inside .NewMessage() you can put specific channel like: chats="test_channel"
async def handler(event):
print('test_channel raw text: ', event.raw_text) #this row is not necessary
msg = event.raw_text
subprocess.call(["python", "s2d.py", msg])
client.run_until_disconnected()
s2d.py #send to discord
import discord, sys
my_secret = '...'
clientdiscord = discord.Client()
#clientdiscord.event
async def on_ready():
#print('We have logged in as {0.user}'.format(clientdiscord)) #this row is not necessary
channel = clientdiscord.get_channel(123456789) # num of channel where you want to write message
msg = sys.argv[1] #grab message
msg = 's2d: ' + msg #only for test, you can delete this row
await channel.send(msg)
quit() # very important quit this bot
clientdiscord.run(my_secret)
It will be a little bit slower (subprocess will make delay), but very easy solution

Discord.py bot leaving voice channel

I've been making a discord bot which enters a vc plays an audio then leaves, but I can't seem to get the leaving part to work. Here is my code:
# Discord specific import
import discord
from discord.ext import commands
import asyncio
Client = discord.Client()
client = commands.Bot(command_prefix="..")
#client.command(pass_context=True)
async def dan(ctx):
author = ctx.message.author
channel = author.voice_channel
vc = await client.join_voice_channel(channel)
player = vc.create_ffmpeg_player('dan.mp3', after=lambda: print('done'))
player.start()
player.disconnect()
client.run('token')
I'm not getting any errors with this but at the same time the bot is not disconnecting from the vc and I have tried changing 'player' for 'client', 'Client' and 'client.voice'
My problems were that:
I needed to use: await vc.disconnect()
My version of websockets was too high and needed to be below v4.0.0
Hope this helps people with my problem
try vc.disconnect() as stated here in the docs, since Client.join_voice_channel(channel) creates a VoiceClient. Also I suggest not having redundant variables like
author = ctx.message.author
channel = author.voice_channel
When you can have vc = await client.join_voice_channel(ctx.message.author.voice_channel)
Also, another redundant variable is Client = discord.Client() since you don't use it anywhere, you use the commands.Bot instance, so it's best to remove that.
player.disconnect() is a coroutine, you should use the await keyword before it.
await player.disconnect()

Resources