I have tried use the EditBannedRequest method for a private channel,
when used it with a client works fine, however, when i use a bot
with administrator permission over the channel, I got this error
Main.telethon.errors.rpcerrorlist.BotMethodInvalidError: The API
access for bot users is restricted. The method you tried to invoke
cannot be executed as a bot (caused by CheckChatInviteRequest)
I handled EditBannedRequest method for receive directly valid
entities without execute get_input_channel and get_input_entity
methods... Then, I printed the values generated for the bot and
compared with values generated for the client, and were equals.
For example:
In the Telegram method, I modified resolve function so:
async def resolve(self, client, utils):
if isinstance(self.channel, InputChannel) and isinstance(self.user_id, InputUser):
self.channel = self.channel
self.user_id = self.user_id
else:
self.channel = utils.get_input_channel(await client.get_input_entity(self.channel))
self.user_id = utils.get_input_user(await client.get_input_entity(self.user_id))
values generated by the client:
InputChannel(channel_id=XXXXXXX, access_hash=XXXXXX)
<class 'Main.telethon.tl.types.InputChannel'>
values sended by the bot:
INVITE_ACCESS = [InputChannel(channel_id=XXXXXXX, access_hash=XXXXXXX)]
USER = [InputUser(user_id=XXXXXXX, access_hash=-XXXXXXX)]
And the function is this:
with TelegramClient(phone, api_id, api_hash).start(bot_token=bot_token) as bot:
result = bot(functions.channels.EditBannedRequest(
channel=INVITE_ACCESS[0],
user_id=USER[0],
banned_rights=types.ChatBannedRights(
until_date=None,
view_messages=True,
send_messages=True
)
))
print(result.stringify())
The final error is this:
Traceback (most recent call last):
File "banneduser.py", line 41, in <module>
client(EditBannedRequest(
File "/usr/local/lib/python3.8/dist-packages/telethon/sync.py", line 39, in syncified
return loop.run_until_complete(coro)
File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "/usr/local/lib/python3.8/dist-packages/telethon/client/users.py", line 30, in _call_
return await self._call(self._sender, request, ordered=ordered)
File "/usr/local/lib/python3.8/dist-packages/telethon/client/users.py", line 77, in _call
result = await future
telethon.errors.rpcerrorlist.ChannelInvalidError: Invalid channel object. Make sure to pass the right types, for instance making sure that the request is designed for channels or otherwise look for a different one more suited (caused by EditBannedRequest)
Any way to make the bot run the EditBannedRequest method without problems in a private channel?
EditBannedRequest can be used by bots just fine, but bots (like the error indicates) cannot use CheckChatInviteRequest.
The access_hash is unique to each account (account A will see person C with hash 123, account B will see person C with has 456).
You should use the channel peer (or marked ID) to let Telethon know you're referring to a channel. Additionally, you should use client.edit_permissions, which is nicer to use than raw API:
chat = types.PeerChannel(123)
# chat = -100123 # equivalent, bot-API style channel ID
# Banning `user` from `chat` forever
await client.edit_permissions(chat, user, view_messages=False)
Related
How do I mark a message as read?
app = Client(session_name, api_id, api_hash)
#app.on_message()
async def my_handler(client, message):
await app.send_message(message.from_user.username, "ok boss")
await app.read_chat_history(message.from_user.username)
app.run()
I expected the bot's message to be ticked that he had read it
Client.read_chat_history()
Mark a chat’s message history as read.
Usable by
[X] Users
[ ] Bots
Parameters:
chat_id (int | str) – Unique identifier (int) or username (str) of the target chat. For your personal cloud (Saved Messages) you can simply use “me” or “self”. For a contact that exists in your Telegram address book you can use his phone number (str).
max_id (int, optional) – The id of the last message you want to mark as read; all the messages before this one will be marked as read as well. Defaults to 0 (mark every unread message as read).
Returns:
bool - On success, True is returned.
EXAMPLE
# Mark the whole chat as read
await app.read_chat_history(chat_id)
# Mark messages as read only up to the given message id
await app.read_chat_history(chat_id, 12345)
I expected the bot's message to be ticked that he had read it
This is not possible, messages send by a Telegram Bot will never get those checkmarks.
Those marks are only for messages send from a user-account
I'd like to use a pub/sub StreamingPullFuture subscription with discordpy to receive instructions for removing users and sending updates to different servers.
Ideally, I would start this function when starting the discordpy server:
#bot.event
async def on_ready():
print(f'{bot.user} {bot.user.id}')
await pub_sub_function()
I looked at discord.ext.tasks but I don't think this use case fits since I'd like to handle irregularly spaced events dynamically.
I wrote this pub_sub_function() (based on the pub/sub python client docs) but it doesn't seem to be listening to pub/sub or return anything:
def pub_sub_function():
subscriber_client = pubsub_v1.SubscriberClient()
# existing subscription
subscription = subscriber_client.subscription_path(
'my-project-id', 'my-subscription')
def callback(message):
print(f"pubsub_message: {message}")
message.ack()
return message
future = subscriber_client.subscribe(subscription, callback)
try:
future.result()
except KeyboardInterrupt:
future.cancel() # Trigger the shutdown.
future.result() # Block until the shutdown is complete.
Has anyone done something like this? Is there a standard approach for sending data/messages from external services to a discordpy server and listening asynchronously?
Update: I got rid of pub_sub_function() and changed the code to this:
subscriber_client = pubsub_v1.SubscriberClient()
# existing subscription
subscription = subscriber_client.subscription_path('my-project-id', 'my-subscription')
def callback(message):
print(f"pubsub_message: {message}")
message.ack()
return message
#bot.event
async def on_ready():
print(f'{bot.user} {bot.user.id}')
await subscriber_client.subscribe(subscription, callback).result()
This works, sort of, but now the await subscriber_client.subscribe(subscription, callback).result() is blocking the discord bot, and returning this error:
WARNING discord.gateway Shard ID None heartbeat blocked for more than 10 seconds.
Loop thread traceback (most recent call last):
Ok, so this Github pr was very helpful.
In it, the user says that modifications are needed to make it work with asyncio because of Google's pseudo-future implementation:
Google implemented a custom, psuedo-future
need monkey patch for it to work with asyncio
But basically, to make the pub/sub future act like the concurrent.futures.Future, the discord.py implementation should be something like this:
async def pub_sub_function():
subscriber_client = pubsub_v1.SubscriberClient()
# existing subscription
subscription = subscriber_client.subscription_path('my-project-id', 'my-subscription')
def callback(message):
print(f"pubsub_message: {message}")
message.ack()
return message
future = subscriber_client.subscribe(subscription, callback)
# Fix the google pseduo future to behave like a concurrent Future:
future._asyncio_future_blocking = True
future.__class__._asyncio_future_blocking = True
real_pubsub_future = asyncio.wrap_future(future)
return real_pubsub_future
and then you need to await the function like this:
#bot.event
async def on_ready():
print(f'{bot.user} {bot.user.id}')
await pub_sub_function()
I am using the Python-Telegram-Bot Package. My telegram bot is unable to receive a message at state FIRST. After sending my bot /start , I am prompted the text message as shown in start_command. However, after sending a url to the bot, the bot is unable to receive the message as seen in the 'single tick' at the bottom right of my message.
# Stages
FIRST, SECOND = range(2)
def start_command(update: Update, context: CallbackContext):
update.message.reply_text("""
To use:
1. Send the google sheets URL and sheet name to this telegram bot in the following format:
your_google_sheets_url (your_sheet_name)
""")
global user_id
user_id = update.message.from_user['id']
return FIRST
def select(update: Update, context: CallbackContext):
if update.message.text:
user_input = update.message.text
update.message.reply_text('I have received your Google Sheets!')
reply_markup = InlineKeyboardMarkup(build_menu(button_list, n_cols=1))
update.message.reply_text('Please choose the headers:', reply_markup=reply_markup)
return SECOND
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start_command))
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start_command)],
states={
FIRST: [MessageHandler(Filters.text, select)],
SECOND: [CallbackQueryHandler(display)],
},
fallbacks=[CommandHandler('cancel', cancel)],
)
dp.add_handler(conv_handler)
the CommandHandler for start should not be added. This is because when the user inputs /start, the CommandHandler is called to handle the command instead of the ConversationHandler.
Is there any way to find a telegram channel id without sending a message to it ?
Right now I go this way and find the channel id by calling this URL in my code and get the JSON as result:
https://api.telegram.org/bot???????/sendMessage?chat_id=#?????&text=123
However, this cause sending the message "123" to the channel which is not good.
Check this link for help.
It is using web telegram url param info.
Adding details from the referred link:
log in web telegram
Click on the target channel then you will find the url displayed on your browser.
If it's a public channel, the ID is #name of the channel.
If it's a private channel then the url must be similar to:
https://web.telegram.org/#/im?p=c1018013852_555990343349619165
For this case, the channel ID would be 1018013852.
It's important to know that channel's IDs are always negative and 13 characters long!
So add -100 to it, making the correct ID -1001018013852.
You Can use Web Telegram to see each channel id in link of that page in your explorer
or
Just Simply Forward a message from your channel to This Bot: (https://telegram.me/getidsbot)
Yes, just forward a message of the channel to your bot and get the message.forward_from_chat.id.
Get your channel link
install python library telethon
get your api_id and api_hash here.
write the following code and run:
from telethon import TelegramClient
api_id=
api_hash=
channel_link = 'your_channel_link'
client = TelegramClient(session_name, api_id, api_hash,
update_workers=4, spawn_read_thread=False)
client.start()
entity = client.get_input_entity(**channel_link**)
print(entity.channel_id)
By the way, if you use it for telegram bot, just add -100 before the printed id, this should be work!
You Don't Need to get Channels ID .
simple send Message with Channel User Like : #channel
Fixing #scruel's answer:
In [1]: api_id = ...
In [2]: api_hash = '...'
In [3]: channelLink = 'https://t.me/BTCST_Community_EN'
In [4]: from telethon import TelegramClient, events
In [5]: client = TelegramClient('test', api_id, api_hash)
In [6]: client.start()
Out[6]: <telethon.client.telegramclient.TelegramClient at 0x7fc90c352290>
In [7]: entity = await client.get_entity(channelLink)
In [8]: channelId = '-100' + str(entity.id)
In [9]: channelId
Out[9]: '-1001236496320'
Here's a CLI util to do it:
#!env/bin/python
import sys
import asyncio
from telethon import TelegramClient
import config
async def channel_id_from_link(client, channel_link):
return "-100" + str((await client.get_entity(channel_link)).id)
async def main(channel_link):
async with TelegramClient(
"test", config.api_id, config.api_hash
) as client:
channel_id = await channel_id_from_link(client, channel_link)
return channel_id
if __name__ == "__main__":
channel_link = sys.argv[1]
channel_id = asyncio.run(main(channel_link))
print(channel_id)
Test:
> ./TelegramChannelId.py https://t.me/binance_api_english
-1001134190352
Put your bot into your channel and go to https://api.telegram.org/bot/getUpdates.
If someone send message to that channel, you will get the channel ID there.
I am building a rest API and am wondering both about HTTP best practices I guess, and how that would apply to the DRF. When sending a PUT request, in the body, do requests have all of the parameters for objects that they would be manipulating? Even if not all of them are changing? Or do they only send the fields that are being updated? So for example if we had a House object with No. of rooms and floors, and I was changing No. of rooms should I only send that parameter, or both of them?
If requests should only contain the fields that are being updating, then how would that translate to the DjangoRestFramework? Any help would be greatly appreciated!
My Views are:
class HouseDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = House.objects.all()
serializer_class = HouseSerializer
and serializer is:
class HouseSerializer(serializers.ModelSerializer):
quotes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = House
fields = (
'pk',
'address',
'quotes',
)
PUT is for full resource updates, PATCH is for partial resource updates because Fielding says so.
Therefore, in your example, if you wanted to only update a No. of rooms for your HouseDetail model, you would send the following payload in a PATCH request:
{ "no. of rooms": "42" }
It is still possible to partially update things with a PUT request in DRF (explained below) but you should simply use PATCH for that because it was created for this. You get PUT and PATCH functionality for your model because you have subclassed the generics.RetrieveUpdateDestroyAPIView class and have defined a serializer.
If a required field is omitted in your PUT request (see documentation for required), then you will receive a 400 status code with a response like {"detail": "x field is required"}. However, when you request via PATCH, there is a partial=True argument passed to the serializer which allows this partial only update. We can see this for the UpdateModelMixin:
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
which calls update, which is:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)