How to disconnect telegram client? - python-3.x

Im trying to write a script in python that listen "first reply" of a bot and then exits. So, I create a client instance and then send a msg to Bot and now I want to record only first reply of bot (upcoming replies can ignored), and save bot reply to Reply variable. Now how to exit from listener mode so that I can do other stuffs after getting reply. I tried client.disconnect() and client.disconnected() but now working or maybe I don't know proper use of these method. I'm new to telethon APIs.
When I run this script, a msg from my telegram is sent to
bot(BotFather) and then bot send a reply
Reply from bot father
I can help you create and manage Telegram bots. If you're new to the
Bot API, please see the manual (https://core.telegram.org/bots).
You can control me by sending these commands:
/newbot - create a new bot /mybots - edit your bots [beta]
Edit Bots /setname - change a bot's name /setdescription - change bot
description /setabouttext - change bot about info /setuserpic - change
bot profile photo /setcommands - change the list of commands
/deletebot - delete a bot
Bot Settings /token - generate authorization token /revoke - revoke
bot access token /setinline - toggle inline mode
(https://core.telegram.org/bots/inline) /setinlinegeo - toggle inline
location requests
(https://core.telegram.org/bots/inline#location-based-results)
/setinlinefeedback - change inline feedback
(https://core.telegram.org/bots/inline#collecting-feedback) settings
/setjoingroups - can your bot be added to groups? /setprivacy - toggle
privacy mode (https://core.telegram.org/bots#privacy-mode) in groups
Games /mygames - edit your games
(https://core.telegram.org/bots/games) [beta] /newgame - create a new
game (https://core.telegram.org/bots/games) /listgames - get a list of
your games /editgame - edit a game /deletegame - delete an existing
game
and this reply got assigned in Reply variable
but my scripts
still listening for other upcoming events. is there any method from
which I can close this connection.
import random
import traceback
import configparser
from telethon import TelegramClient, events, sync
from telethon.errors import SessionPasswordNeededError
from telethon.errors.rpcerrorlist import PeerFloodError
from telethon.tl.functions.channels import InviteToChannelRequest
from telethon.tl.functions.messages import GetDialogsRequest,GetHistoryRequest
from telethon.tl.types import InputPeerEmpty, InputPeerChannel, InputPeerUser, PeerChannel
api_id = #Api_ID
api_hash = #Api_Hash
phone = #session
client = TelegramClient(phone, api_id, api_hash)
Reply = ' '
#client.on(events.NewMessage(chats='https://t.me/BotFather'))
async def NewMessageListener(event):
Reply = event.message.message
with client:
client.send_message("https://t.me/BotFather", "/start")
client.run_until_disconnected()
# Disconnect client to stop run_until_disconnected()
# Do other stuff!!!

I don't understand what you trying to achieve here but you can disconnect the client using disconnect method
from telethon import TelegramClient, events
api_id = #Api_ID
api_hash = #Api_Hash
phone = #session
client = TelegramClient(phone, api_id, api_hash)
Reply = ' '
#client.on(events.NewMessage(chats='https://t.me/BotFather'))
async def newMessageListener(event):
reply = event.message.message
# do stuff with reply then close the client
await client.disconnect()
async def main():
async with client:
await client.send_message("https://t.me/BotFather", "/start")
await client.run_until_disconnected()

Related

Echo a message from another app in discord

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

Secret chat session in python telethon

How can i start a secret chat session using telethon? I've found a solution on the web; https://pypi.org/project/telethon-secret-chat/, but i'm not sure how it is supposed to work. I understand all the other stuff but what is supposed to go inside in the manager.start_secret_chat(target) function?
from telethon import TelegramClient
from telethon_secret_chat import SecretChatManager
client = TelegramClient('log', app_id, app_hash)
async def replier(event):
# all events are encrypted by default
if event.decrypted_event.message and event.decrypted_event.message == "hello":
await event.reply("**hi**") # parse_mode is markdown by default
async def new_chat(chat, created_by_me):
if created_by_me:
print("User {} has accepted our secret chat request".format(chat))
else:
print("We have accepted the secret chat request of {}".format(chat))
manager = SecretChatManager(client, auto_accept=True,
new_chat_created=new_chat) # automatically accept new secret chats
manager.add_secret_event_handler(func=replier) # we can specify the type of the event
with client:
client.run_until_disconnected()
manager.start_secret_chat(target)
Target can be the user_id. See this example:
# wait for incoming message
#client.on(events.NewMessage)
async def incoming_message_handler(event):
userId = event.original_update.user_id
# start secret chat
await manager.start_secret_chat(userId)

Discord.py welcome message for multiple servers

I am making a discord bot that I plan on being in multiple servers. Each server will have a different welcome channel name and all that. I made the welcome message and I tried making the bot post the message in a channel called "welcome" which would solve this problem but didn't work. I thought about making a database that saves the channel id that the server owner sends to the bot under the server name/ID. The bot when triggered would match the server ID to one in the database then grab the channel id linked to the server id. But that would be a lot of coding in SQL or PostgreSQL which I would have to learn how to get the bot to save the sever id and channel id to the database, How to get the bot to match the server id's then grab the channel id and posting it the message to the server. There is no documentation on discord py bots and making welcome messages for different servers. I was wondering if there is a better way to do it and how would I do it?
What I have so far in relation to the welcome message.
import discord
import logging
import asyncio
import random
import time
import tweepy, discord
from discord.ext import commands
from discord.ext.commands import bot
#File Imports
from config import *
client = commands.Bot(command_prefix='sec.')
# logger = logging.getLogger('discord')
# logger.setLevel(logging.DEBUG)
# handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
# handler.setFormatter(logging.Formatter('%(name)s: %(message)s'))
# logger.addHandler(handler)
#client.event
async def on_ready():
print('Logged in as %s' % client.user.name)
while True:
presence = random.choice(['sec.help', 'Defending Servers'])
activity = discord.Game(name=(presence))
await client.change_presence(status=discord.Status.online, activity=activity)
await asyncio.sleep(7)
client.remove_command('help')
#client.event
async def on_member_join(member):
# Adds role to user
# role = discord.utils.get(member.server.roles, name='Member')
# await client.add_roles(member, role)
# Random embed color
range = [255,0,0]
rand = random.shuffle(range)
# Welcomes User
embed = discord.Embed(title="{}'s info".format(member.name), description="Welcome too {}".format(member.guild.name))
embed.add_field(name="Name", value=member.name, inline=True)
embed.add_field(name="ID", value=member.id, inline=True)
embed.add_field(name="Status", value=member.status, inline=True)
embed.add_field(name="Roles", value=member.top_role)
embed.add_field(name="Joined", value=member.joined_at)
embed.add_field(name="Created", value=member.created_at)
embed.set_thumbnail(url=member.avatar_url)
inlul = client.get_channel(CHANNEL_ID)
await inlul.send(inlul, embed=embed)
If you find any documentation on this I would love to read it. All I could find are for bots that are basic and has you enter a channel id.
If the bot is on a much smaller scale, say just a few servers, then I'd say using a json file to save a dictionary wouldn't be a bad idea.
You can save the Id of the top text channel as a default when the server joins the server and let them change what channel to use with commands, this can be done with the on_guild_join event
import json
#sets value in json to guild id upon the bot joining the guild
#client.event
async def on_guild_join(guild):
#loads json file to dictionary
with open("filename.json", "r") as f:
guildInfo = json.load(f)
guildInfo[guild.id] = guild.text_channels[0] #sets key to guilds id and value to top textchannel
#writes dictionary to json file
with open("filename.json", "w") as f:
json.dump(guildInfo, f)
#allows server members to set channel for welcome messages to send to
#client.command()
async def welcomeMessage(ctx):
with open("filename.json", "r") as f:
guildInfo = json.load(f)
guildInfo[ctx.message.guild.id] = ctx.message.channel.id #sets channel to send message to as the channel the command was sent to
with open("filename.json", "w") as f:
json.dump(guildInfo, f)
then just use
with open("filename.json", "r"):
guildInfo = json.load(f)
channnel = guildInfo[ctx.message.guild.id]
to get the channel to send the message to and
channel.send(embed=embed)
to send the message
before running it ensure to have an empty json file in the same directory and add {} to the file.

How can I send a message to #BotFather programmatically?

I know that a bot can be created in a telegram if you send the command /newbot to the bot #BotFather. But to do this, you need to take your device, open Telegram, open a chat with #BotFather and send him the command /newbot. Can all of the above be done programmatically?
P.S.: this is not laziness, but an attempt to optimize the solution.
Yes, it's possible to create such interaction with mtproto libraries (pyrogram, telethon, madelineproto, etc...)
Here is a PoC script using telethon (python3 -m pip install -U telethon first to install the dependency):
from telethon import TelegramClient, events
api_id = ...
api_hash = "..."
client = TelegramClient('session', api_id, api_hash)
BOT_NAME="..."
BOT_USER_NAME="..." # must end with -bot
#client.on(events.NewMessage)
async def message_handler(event):
if 'Please choose a name for your bot' in event.raw_text:
await event.reply(BOT_NAME)
elif 'choose a username for your bot' in event.raw_text:
await event.reply(BOT_USER_NAME)
elif 'Done! Congratulations on your new bot' in event.raw_text:
print("Bot created!")
await client.disconnect()
async def main():
await client.send_message('botfather', '/newbot')
with client:
client.loop.run_until_complete(main())
client.run_until_disconnected()
You acquire the app_id and app_hash values from https://my.telegram.org/
and here is telethon's doc.

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