Echo a message from another app in discord - python-3.x

I have a bot running in a separate app, but there is a specific variable holding data that I want to also be echo'd on my discord server. The bot itself is huge, but I can pin the specific method here
import discord
import asyncio
import rpChat
global note
class EchoBot(rpChat.ChatClient):
def on_PRI(self, character, message):
super().on_PRI(character, message)
note = character + ": " + message
global note
if message[:1] == "~":
super().PRI("A GameMaster", note)
to try to send this message to discord, I have the following. This is not put in the class above, but just below it, and is not in a class itself:
client = discord.Client()
async def discordEcho():
"""Background task that sends message sent from chat to an appropriate Discord channel
when global messege data is updated"""
await client.wait_until_ready()
channel = client.get_channel(serverroom}
while not client.is_closed():
global note
await channel.send(channel, note)
The data to grab the channel IDs are found in a json
file = open("credentials.json", "r", encoding="utf-8")
info = json.load(file)
file.close()
token = info["discord"]
serverroom = info["serverroom"]
client.loop.create_task(discordEcho())
client.run(token)
When I try this, I obtain:
await client.send_message(channel, message)
AttributeError: 'Client' object has no attribute 'send_message'
And I am unsure why. I have built a bot for both this chat platform, and for discord, but this is the first time I've ever attempted to bridge messages between the two. I am sure what I have offered is clear as mud, but any help would be appreciated, if possible.
Edit:
Edited changes to code. It is working thanks to comment below.

client.send_message is an old, outdated method, it has been replaced with Messageable.send
async def discordEcho():
"""Background task that sends message sent from chat to an appropriate Discord channel
when global messege data is updated"""
await client.wait_until_ready()
channel = client.get_channel(serverroom)
while not client.is_closed():
global note
await channel.send(note)
Side note: you first need to wait_until_ready before getting the channel to wait until the cache is done loading, otherwise channel will be None

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).

Python3 + Discord - Send a message each X seconds

I am trying to create a Discord bot that would automatically send a message each X seconds (every 3 seconds, for example) without any user interaction/command input.
This is the code that I have:
import discord
from discord.ext import tasks, commands
bot = commands.Bot(command_prefix="!")
#bot.event
async def on_ready():
await bot.wait_until_ready()
print("Logged in as:")
print(bot.user.name)
print("------")
channel = bot.get_channel(IDasInteger)
print("Channel is:")
print(channel) #Prints None
get_price.start()
#tasks.loop(seconds=3)
async def get_price():
await bot.wait_until_ready()
channel = bot.get_channel(IDasInteger)
print("Channel is:")
print(channel) #Prints None
await channel.send('Test')
#get_price.before_loop
async def before ():
print("Before done.")
bot.run('MyTokenHere')
The problem is, that when I execute this code, it gives me the following error:
AttributeError: 'NoneType' object has no attribute 'send'
When I try to print the channel variable it returns None.
The Channel ID is correct - I have copied it directly from Discord app without any alterations to its value.
Any ideas, please?
Thank you
The reason you are getting None is that IDasInteger is not defined (at least in the code you provided. If it is defined in your code then it cannot find the channel and returns None. None does not have the attribute send and the error is raised.
Change the channel id to the id of a valid channel in your server.
functions channel = bot.get_channel(790279425633758682)
Or make sure the IDasInteger is returning a valid channel ID
Note: I tested the code with a valid channel id to my server and it worked as intended.

TwitchIO: Delete a single message in channel

How can I delete a single message sent by a user using TwitchIO?
#bot.event
async def event_message(ctx):
await ctx.content.delete() # Does not work
await ctx.content.remove() # Does not work
await ctx.channel.timeout(ctx.author, 1) # Does not work
The question is older, but I'll answer it anyway.
Twitchio doesn't directly support this.
But you can delete individual messages in Twitch Chat, see the Twitch IRC documentation.
CLEARMSG (Twitch Commands)
You need the message ID for this. You get the ID in the message tags.
Message tags
Code example:
async def event_message(message):
if not message.author.name == self.bot.nick:
message_id = message.tags['id']
await message.channel.send(f"/delete {message_id}")
If you want to timeout someone, do the following:
await message.channel.timeout(message.author.name, 120, f"reason")
Twitchio documentation Channel.timeout

How do I add a reaction emoji to the message the bot sends after doing the user's command?

I get this error: discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Bot' object has no attribute 'message'--when trying to do await self.client.message.add_reaction(emoji).
I tried changing it to await ctx.message.add_reaction(emoji), and I realized that it reacted to the command the user sent rather than the bot's new message.
import discord
from discord.ext import commands
class MovieNight(commands.Cog):
"""Polls for Movie Night."""
def __init__(self, client):
self.client = client
#commands.command(aliases=['m'])
async def movie(self, ctx, year, *movie):
movie_title = ' '.join(movie[:-1])
await ctx.send(f"`{year}` - `{movie_title}` | **{movie[-1]}**")
emoji = '👎'
await self.client.message.add_reaction(emoji)
def setup(client):
client.add_cog(MovieNight(client))
self.client doesn't know about the message, the is stored as part of the invocation context:
await ctx.message.add_reaction(emoji)
Adding onto Patrick's answer here from what you said in the comment.
await self.client.message.add_reaction(emoji) won't work because the bot doesn't know what message you're referring to, and client doesn't have an attribute called message.
Adding reactions requires a discord.Message object, which in your case can be either the command that the user executed (e.g. !movie 2020 movie title) which you can retrieve via ctx.message, or a message which you're making the bot send.
If you wanted to get the message object from the message that the bot sent, you can assign it to a variable:
msg = await ctx.send(f"`{year}` - `{movie_title}` | **{movie[-1]}**")
And this allows you to then add a reaction to it or access any other message attributes you would like:
emoji = '👎'
await msg.add_reaction(emoji)
References:
discord.Message
Message.add_reaction()
TextChannel.send() - Here you can see it returns the message that was sent

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

Resources