So I am using Python 3 to create a script with Telegram API but have come across a pickle.
Essentially I want anything I post to one channel to automatically be posted to a list of other channels.
As there are a lot of channels I am pretty sure I have split it over multiple lines, keeping the variables aligned with the first delimiter as per PEP8 code layout advice. However, for some reason, the code only posts content to the first line of channels in the list and not the others.
Right now my code is:
from telethon import TelegramClient, events
api_id = 9969279 #API_ID
api_hash = 'd5fa4ca47bcdb6f5bda7271f30e607e9' #API_HASH
channel = [-1001612029593] #MAIN_CHANNEL_ID
to_channel = [-1001408569373,-1001450011693,-1001589562089,-1001324581893,-1001486742214,-1001262799053,-1001577547504,-1001751417089,
-1001714682472,-1001173439993,-1001564319953,-1001458872203,-1001580609047,-1001454607304,-1001412291633,-1001307551599,
-1001472656059,-1001577461465,-1001577461465,-1001438912928,-1001166100393,-1001226585647,-1001662052611,-1001412291633,
-1001213093519,-1001543013350,-1001599148038,-1001250770087,-1001489822613,-1001586463068,-1001173726857,-1001425575134,
-1001549263406,-1001566043627,-1001605175905,-1001229590696,-1001442156928] #OTHER_CHANNELS
client = TelegramClient('TGBot', api_id, api_hash)
#client.on(events.NewMessage(chats=channel))
async def my_event_handler(event):
if event.message:
for channelx in to_channel:
await client.send_message(channelx, event.message)
client.start()
client.run_until_disconnected()
I'm wondering as to whether the lines are too long - other than that I can't think of why it won't post all channels?
EDIT: When running the script I get the following notes on my CMD
Traceback (most recent call last):
File "C:\Users\mimif\AppData\Local\Programs\Python\Python310\lib\site-packages\telethon\client\updates.py", line 451, in _dispatch_update
await callback(event)
File "C:\Users\mimif\OneDrive\Desktop\python\main.py", line 18, in my_event_handler
await client.send_message(channelx, event.message)
File "C:\Users\mimif\AppData\Local\Programs\Python\Python310\lib\site-packages\telethon\client\messages.py", line 825, in send_message
return await self.send_file(
File "C:\Users\mimif\AppData\Local\Programs\Python\Python310\lib\site-packages\telethon\client\uploads.py", line 412, in send_file
return self._get_response_message(request, await self(request), entity)
File "C:\Users\mimif\AppData\Local\Programs\Python\Python310\lib\site-packages\telethon\client\users.py", line 30, in __call__
return await self._call(self._sender, request, ordered=ordered)
File "C:\Users\mimif\AppData\Local\Programs\Python\Python310\lib\site-packages\telethon\client\users.py", line 84, in _call
result = await future
telethon.errors.rpcerrorlist.ChannelPrivateError: The channel specified is private and you lack permission to access it. Another reason may be that you were banned from it (caused by SendMediaRequest)```
Related
I run a small python bot that requires data from Github. To not get blocked by Github I download the data and automatically update it every week. I would like to get notified via a discord dm in case the bot couldn't connect to Github and update it's data.
I already have a check for if the bot couldn't update it's data and now all I need is that the bot sends me a dm. Because of schedule I run the automatic updater in a thread.
The problem I face is that this results in: AttributeError: 'NoneType' object has no attribute 'request' in the guild = await bot.fetch_guild(GUILD_ID) line and I don't see why it doesn't work. I already created a little test bot to test if the notify_on_no_respond function works and if triggered by a command from discord it works perfectly.
Full Error:
Exception in thread Thread-1 (start_updater):
Traceback (most recent call last):
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
self.run()
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_auto_updater.py", line 7, in start_updater
Thread_Auto_Updater = auto_updater(
File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_auto_updater.py", line 22, in __init__
self.data['Bot'].problem_occured()
File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_main.py", line 70, in problem_occured
asyncio.run(notify_on_no_respond())
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete
return future.result()
File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_main.py", line 74, in notify_on_no_respond
guild = await bot.fetch_guild(GUILD_ID)
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\client.py", line 1188, in fetch_guild
data = await self.http.get_guild(guild_id)
File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\http.py", line 192, in request
async with self.__session.request(method, url, **kwargs) as r:
AttributeError: 'NoneType' object has no attribute 'request'
If anyone knows how to fix this problem I would love to hear it. Thanks in advance.
If you need anything from me to help you help me ask and I will try to provide it.
This replicates the code I use but for some reason creates a different error: (you will have to provide your own token etc. in an .env)
# Bot_Game_main.py
import Bot_Game_auto_updater
import os
import discord
import threading
import asyncio
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
MY_ID = os.getenv('DISCORD_MY_ID')
GUILD_ID = os.getenv('DISCORD_GUILD_ID')
bot = commands.Bot(command_prefix='/')
class maintainer(object):
def __init__(self):
pass
def start_thread(self, Bot_Game):
threading.Thread(target=Bot_Game_auto_updater.start_updater,
args=(Bot_Game,), daemon=True).start()
def problem_occured(self):
asyncio.run(notify_on_no_respond())
async def notify_on_no_respond():
guild = await bot.fetch_guild(GUILD_ID)
user = await guild.fetch_member(MY_ID)
await user.send('a problem occured')
Bot_Game = maintainer()
Bot_Game.start_thread(Bot_Game)
bot.run(TOKEN)
# Bot_Game_auto_updater.py
def start_updater(Bot_Game):
Thread_Auto_Updater = auto_updater(Bot_Game)
class auto_updater(object):
def __init__(self, Bot_Game):
Bot_Game.problem_occured()
First of all to you main Problem: I would recommend not using that code, there is a way better way to do so with the get_user method:
from discord.utils import get
user = await client.get_user(user_id)
To DM a user I use this code:
channel = await user.create_dm()
await channel.send("a problem occured")
Hope this helped :D
i have a bot which parses some links given by user. When clients want to parse realy huge amount of links, bot parses them, creates csv file from those links, sends it to user(user can download and view this file) and then raise TimeoutError
Cause exception while getting updates.
Traceback (most recent call last):
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiogram/dispatcher/dispatcher.py", line 381, in start_polling
updates = await self.bot.get_updates(
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiogram/bot/bot.py", line 110, in get_updates
result = await self.request(api.Methods.GET_UPDATES, payload)
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiogram/bot/base.py", line 231, in request
return await api.make_request(await self.get_session(), self.server, self.__token, method, data, files,
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiogram/bot/api.py", line 139, in make_request
async with session.post(url, data=req, **kwargs) as response:
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiohttp/client.py", line 1138, in __aenter__
self._resp = await self._coro
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiohttp/client.py", line 559, in _request
await resp.start(conn)
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiohttp/client_reqrep.py", line 913, in start
self._continue = None
File "/Users/alex26/miniforge3/envs/rq/lib/python3.8/site-packages/aiohttp/helpers.py", line 721, in __exit__
raise asyncio.TimeoutError from None
asyncio.exceptions.TimeoutError
Some example how my bot looks like(that's not real code, that's EXAMPLE):
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
def parse_1000_links():
#it takes about 10 mins and therefore that's the same like sleep(600)
sleep(600)
#dp.message_handler()
async def msg_handler(message: types.Message):
if msg == 'parse 1000 links':
res = parse_1000_links()
create_csv_from_res(res)
file_open_obj = open('data.csv', 'rb')
await bot.send_document(message.from_user.id, file_open_obj)
.....
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
User can even get from bot this final file, but after sending this file to user, bot raises this error. That's strange. If user get message, it means that everything is fine(That's my opinion)
How to fix my issue?
Thanks
You should avoid using blocking operations, cause they freeze ALL event loop.
If you can't use async version of your dependency, use executor:
https://docs.python.org/3/library/asyncio-eventloop.html#executing-code-in-thread-or-process-pools
I'm making a discord bot and one of my functions is going to be getting the source image for an emoji for... thieving purposes.
I have created a simple function to get the URL for the emoji sent in the command and return that in chat, which gets converted by discord automatically into the emoji source image. Perfect!
BUT! It doesn't work on emojis from outside the server. Emojis from within the server work perfectly fine, including animated ones, but emojis (both static and animated) from outside the server do not get returned.
My current code goes as follows:
#bot.command(brief='Allows you to steal an emoji', description='Will return the image file for a requested emoji', usage='[emoji](can be from anywhere and animated if you have nitro)')
async def steal(ctx, emoji: discord.Emoji):
receivedcommand(ctx)
await ctx.send(emoji.url)
How can I change this code to work for emojis not within the server that the bot's in? I assume I gotta do some funky shit with IDs but I don't know where to start.
Help?
Edit: Error message
Ignoring exception in command steal:
Traceback (most recent call last):
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\bot.py", line 939, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 855, in invoke
await self.prepare(ctx)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 789, in prepare
await self._parse_arguments(ctx)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 697, in _parse_arguments
transformed = await self.transform(ctx, param)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 552, in transform
return await self.do_conversion(ctx, converter, argument, param)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 505, in do_conversion
return await self._actual_conversion(ctx, converter, argument, param)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 451, in _actual_conversion
ret = await instance.convert(ctx, argument)
File "C:\Users\Vyladence\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\converter.py", line 723, in convert
raise EmojiNotFound(argument)
discord.ext.commands.errors.EmojiNotFound: Emoji "<:10_years:578822331766538240>" not found.
For external emojis, you need to use discord.PartialEmoji:
#bot.command(brief='Allows you to steal an emoji', description='Will return the image file for a requested emoji', usage='[emoji](can be from anywhere and animated if you have nitro)')
async def steal(ctx, emoji: discord.PartialEmoji):
receivedcommand(ctx)
await ctx.send(emoji.url)
If you use discord.Emoji, your bot needs to be in the guild the emoji comes from.
What is the way to close the discord.py bot loop once tasks are done?
Added:
nest_asyncio.apply()
Tried:
bot.logout(), bot.close(), bot.cancel()
Code:
async def helper(bot, discord_user_feed):
nest_asyncio.apply()
await bot.wait_until_ready()
# await asyncio.sleep(10)
for each_id, events_list in discord_user_feed.items():
discord_user = await bot.fetch_user(int(each_id))
for each_one in events_list:
msg_sent = True
while msg_sent:
await discord_user.send(each_one)
msg_sent = False
await bot.close()
return 'discord messages sent'
async def discord_headlines(request):
nest_asyncio.apply()
discord_user_feed = await get_details()
bot = commands.Bot(command_prefix='!')
bot.loop.create_task(helper(bot, discord_user_feed))
message = bot.run('my_token')
return HttpResponse(message)
I can be able to send messages to discord users using id and discord bot. But, even after, django view api is continuously running and getting the error. It needs to return a response message - discord messages sent.
Error:
Exception in callback <TaskWakeupMethWrapper object at 0x000001C852D2DA38>(<Future finis...SWdZVtXT7E']}>)
handle: <Handle <TaskWakeupMethWrapper object at 0x000001C852D2DA38>(<Future finis...SWdZVtXT7E']}>)>
Traceback (most recent call last):
File "E:\sd\envs\port\lib\asyncio\events.py", line 145, in _run
self._callback(*self._args)
KeyError: <_WindowsSelectorEventLoop running=True closed=False debug=False>
Internal Server Error: /api/v0/discord_headlines/
Traceback (most recent call last):
File "E:\sd\envs\port\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "E:\sd\envs\port\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "E:\sd\envs\port\lib\site-packages\asgiref\sync.py", line 139, in __call__
return call_result.result()
File "E:\sd\envs\port\lib\concurrent\futures\_base.py", line 425, in result
return self.__get_result()
File "E:\sd\envs\port\lib\concurrent\futures\_base.py", line 384, in __get_result
raise self._exception
File "E:\sd\envs\port\lib\site-packages\asgiref\sync.py", line 204, in main_wrap
result = await self.awaitable(*args, **kwargs)
File "E:\back-end\port_api\views\discord_integration.py", line 110, in discord_headlines
message = bot.run('my_token')
File "E:\sd\envs\port\lib\site-packages\discord\client.py", line 719, in run
_cleanup_loop(loop)
File "E:\sd\envs\port\lib\site-packages\discord\client.py", line 95, in _cleanup_loop
loop.close()
File "E:\sd\envs\port\lib\asyncio\selector_events.py", line 107, in close
raise RuntimeError("Cannot close a running event loop")
RuntimeError: Cannot close a running event loop
[13/Jun/2021 01:07:34] "GET /api/v0/discord_headlines/ HTTP/1.1" 500 109010
These are the versions:
Python 3.6.4, Django 3.1, discord.py 1.7.3, Asyncio, Anaconda-4.9.2, Windows-10
I've not used older versions so I can't attest to the accuracy of this answer for pre-2021 releases (v1.6.0+) Discord.py.
When starting a discord Client or Bot via run(*args, **kwargs) there is actually no class method to stop it.
Per the Discord.py Documentations Discord.py discord.Client.run:
A blocking call that abstracts away the event loop initialisation from
you.
If you want more control over the event loop then this function should
not be used. Use start() coroutine or connect() + login()
And it's effectively equal to:
# run(*args, **kwargs) is basically
try:
# Start the bot on asyncio event loop with using .start() coroutine
loop.run_until_complete(start(*args, **kwargs))
except KeyboardInterrupt:
# Invoke close() coroutine on the same loop if interrupt
loop.run_until_complete(close())
# Cancel any other lingering tasks and such
finally:
loop.close()
So the unrewarding but accurate answer with your current attempted implementation is simply... you can't.
If you wanna drop some more info on the webserver library your using in response or via a message, I can help craft you something functional.
If your webserver is fully asyncio integrated, your best bet is seeing if the library has a way to add tasks to its existing event loop, or if you pass one to it. Sanic allows for this in its decorator startup/shutdown methods (ex. after_server_start, etc). Store the reference to the bot instance somewhere and pass it around as needed.
I'll try and keep an eye on here for a response or DM and try to help get you functional and we can post updated code answer here, there's a big lack of solid answers for using discord.py in more complex cases within other applications/webservices and I just ran into a similiar issue with writing a discord.py bot that accepted webhook data and posted embeds to appropriate channels based on some filters.
My code is simple because I am new to coding my discord bot, I want it so the bot sends a random meme from a subreddit when the word !meme is sent in discord, here is the code:
import discord
import os
import praw
import random
client = discord.Client()
reddit = praw.Reddit(client_id='the client id',
client_secret='the client secret',
user_agent='Memri TV Bot by /u/Hezbolloli')
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('$hello'):
subreddit = reddit.subreddit("memritvmemes")
all_subs = []
top = subreddit.hot(limit=50)
for submission in top:
all_subs.append(submission)
random_sub = random.choice(all_subs)
name = random_sub.title
url = random_sub.url
em = discord.Embed(title=name)
em.set_image(url=url)
await ctx.send(embed=em)
client.run('Token')
My error is here and I am not sure where I should start looking at this because this is by far the longest error I have ever got in my coding career:
It appears that you are using PRAW in an asynchronous environment.
It is strongly recommended to use Async PRAW: https://asyncpraw.readthedocs.io.
See https://praw.readthedocs.io/en/latest/getting_started/multiple_instances.html#discord-bots-and-asynchronous-environments for more info.
Ignoring exception in on_message
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "main.py", line 28, in on_message
for submission in top:
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/praw/models/listing/generator.py", line 63, in __next__
self._next_batch()
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/praw/models/listing/generator.py", line 73, in _next_batch
self._listing = self._reddit.get(self.url, params=self.params)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/praw/reddit.py", line 566, in get
return self._objectify_request(method="GET", params=params, path=path)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/praw/reddit.py", line 666, in _objectify_request
self.request(
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/praw/reddit.py", line 848, in request
return self._core.request(
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/sessions.py", line 324, in request
return self._request_with_retries(
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/sessions.py", line 222, in _request_with_retries
response, saved_exception = self._make_request(
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/sessions.py", line 179, in _make_request
response = self._rate_limiter.call(
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/rate_limit.py", line 33, in call
kwargs["headers"] = set_header_callback()
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/sessions.py", line 277, in _set_header_callback
self._authorizer.refresh()
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/auth.py", line 346, in refresh
self._request_token(grant_type="client_credentials")
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/auth.py", line 155, in _request_token
response = self._authenticator._post(url, **data)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/prawcore/auth.py", line 38, in _post
raise ResponseException(response)
prawcore.exceptions.ResponseException: received 401 HTTP response
It seems the problem lies in your request to Reddit. As you are working in a asynchronous environment, you need to use the asyncpraw module instead (docs: Asyncpraw for reddit docs). The code is almost the same:
import discord
import os
import asyncpraw # install it using "pip install asyncpraw"
import random
client = discord.Client()
reddit = asyncpraw.Reddit(client_id='the client id',
client_secret='the client secret',
user_agent='Memri TV Bot by /u/Hezbolloli')
Also, you did not define ctx. Try : message.channel.send(...), or setup a bot.
If you are coding a bot, I would also strongly recommend that you use the discord bot commands as a way of adding functions, instead of reading the messages content (you could also use slash commands).
Here is a link to the docs : https://discordpy.readthedocs.io/en/stable/#getting-started
There is quite a lot of info about that online. If you run into trouble I'd be glad to help.