How to loop inside a custom Telegram bot? - telegram-bot

We are trying to make a telegram price bot but running into an issue that could be solved using third party code, however we can't set the bot to send us the updated price every 5 minutes (or more) WITHOUT USING THIRD PARTY SOLUTIONS for security reasons.
How to loop from INSIDE this code, without using another third party Telegram bot?
Here is the code
import telegram
from telegram.ext import Updater
from telegram.ext import CommandHandler
from tracker import get_prices
telegram_bot_token = "mybot"
updater = Updater(token=telegram_bot_token, use_context=True)
dispatcher = updater.dispatcher
def start(update, context):
chat_id = update.effective_chat.id
message = ""
crypto_data = get_prices()
for i in crypto_data:
coin = crypto_data[i]["coin"]
price = crypto_data[i]["price"]
change_day = crypto_data[i]["change_day"]
change_hour = crypto_data[i]["change_hour"]
message += f" {coin}={price:,.5f}$ \nHour Change: {change_hour:.3f}%\nDay Change: {change_day:.3f}%\n\n"
context.bot.send_message(chat_id=chat_id, text=message)
dispatcher.add_handler(CommandHandler("start", start))
updater.start_polling()
Any solution that correctly sends one message at a time without appending to the previous one? Thanks!

There are different ways to do this.
The first would be with a simple time.sleep() in a while loop:
import time
def start(update, context):
chat_id = update.effective_chat.id
while True:
message = ""
crypto_data = get_prices()
for i in crypto_data:
coin = crypto_data[i]["coin"]
price = crypto_data[i]["price"]
change_day = crypto_data[i]["change_day"]
change_hour = crypto_data[i]["change_hour"]
message += f" {coin}={price:,.5f}$ \nHour Change:{change_hour:.3f}%\nDay Change: {change_day:.3f}%\n\n"
context.bot.send_message(chat_id=chat_id, text=message)
time.sleep(300)
Another method might be using a background process scheduler, but you would probably refactor your start function and only schedule the part that creates/sends the message. (The part inside the while loop)
Advanced Python Scheduler (pip install apscheduler) is a fantastic library for this, but it is a third party library, so maybe not appropriate for you. I have used it on many projects however.
EDIT:
Here's an example of scheduling with apscheduler:
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
def message_loop(chat_id, bot):
message = ""
crypto_data = get_prices()
for i in crypto_data:
coin = crypto_data[i]["coin"]
price = crypto_data[i]["price"]
change_day = crypto_data[i]["change_day"]
change_hour = crypto_data[i]["change_hour"]
message += f" {coin}={price:,.5f}$ \nHour Change: {change_hour:.3f}%\nDay Change: {change_day:.3f}%\n\n"
bot.send_message(chat_id=chat_id, text=message)
def start(update, context):
chat_id = update.effective_chat.id
bot = context.bot
scheduler.add_job(message_loop, 'interval', minutes=5, args=(chat_id, bot))
scheduler.start()
# You might want to also add a stop function to your bot:
def stop():
scheduler.shutdown()
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("stop", stop))
updater.start_polling()

you should try to add setinterval or a pure millis() function and you will be good to go

Related

I want my telegram bot to send messages at the exact time but when I run my code the message sends immediately and no errors pop up

This is the part of the code where I tried to use scheduler
scheduler = BackgroundScheduler(timezone="Europe/Istanbul")
#bot.message_handler(content_types=\['text', \])
def get_name(message):
keyboard = types.InlineKeyboardMarkup()
cont = types.InlineKeyboardButton(text='re.README', callback_data='yes3')
keyboard.add(cont)
current_user = message.chat.username
res = engine.execute(f"SELECT COUNT(\*) FROM members WHERE username = '{current_user}'").fetchall()\[0\]\[0\]
if res == 0:
engine.execute(f'''INSERT INTO members (username, user_id, name, score) VALUES ('{current_user}', '{message.chat.id}', '{message.text}', '{0}');''')
bot.send_message(message.chat.id, 're.README')
#bot.message_handler(content_types=\['text',\])
def prom(message):
bot.send_message(message.chat.id, 'gyuk')
scheduler.add_job(prom, 'date', run_date=datetime(2023, 1, 20, 14, 30))
if __name__ == '__main__':
try:
scheduler.start()
bot.polling(none_stop=True)
while True:
sleep(1)
except:
pass
I tried different suggestions from here but it still doesn't work
In that case, the bot doesn't know the chat.id parameter where you want to send the message, because no message where sent to him.
If you want to schedule the sending of a message you have to iterate over the database of the people that have activated the bot correctly, and from it, you have to select the chat.id parameter so the bot knows the chat in which sending the message.
Furthermore, the scheduled function does not have to be a handler otherwise the bot will wait for a message to activate that function, instead of the scheduler.

how to read the console output in python without executing any command

I have an API which gets the success or error message on console.I am new to python and trying to read the response. Google throws so many examples to use subprocess but I dont want to run,call any command or sub process. I just want to read the output after below API call.
This is the response in console when success
17:50:52 | Logged in!!
This is the github link for the sdk and documentation
https://github.com/5paisa/py5paisa
This is the code
from py5paisa import FivePaisaClient
email = "myemailid#gmail.com"
pw = "mypassword"
dob = "mydateofbirth"
cred={
"APP_NAME":"app-name",
"APP_SOURCE":"app-src",
"USER_ID":"user-id",
"PASSWORD":"pw",
"USER_KEY":"user-key",
"ENCRYPTION_KEY":"enc-key"
}
client = FivePaisaClient(email=email, passwd=pw, dob=dob,cred=cred)
client.login()
In general it is bad practice to get a value from STDOUT. There are some ways but it's pretty tricky (it's not made for it). And the problem doesn't come from you but from the API which is wrongly designed, it should return a value e.g. True or False (at least) to tell you if you logged in, and they don't do it.
So, according to their documentation it is not possible to know if you're logged in, but you may be able to see if you're logged in by checking the attribute client_code in the client object.
If client.client_code is equal to something then it should be logged in and if it is equal to something else then not. You can try comparing it's value when you successfully login or when it fails (wrong credential for instance). Then you can put a condition : if it is None or False or 0 (you will have to see this by yourself) then it is failed.
Can you try doing the following with a successful and failed login:
client.login()
print(client.client_code)
Source of the API:
# Login function :
# (...)
message = res["body"]["Message"]
if message == "":
log_response("Logged in!!")
else:
log_response(message)
self._set_client_code(res["body"]["ClientCode"])
# (...)
# _set_client_code function :
def _set_client_code(self, client_code):
try:
self.client_code = client_code # <<<< That's what we want
except Exception as e:
log_response(e)
Since this questions asks how to capture "stdout" one way you can accomplish this is to intercept the log message before it hits stdout.
The minimum code to capture a log message within a Python script looks this:
#!/usr/bin/env python3
import logging
logger = logging.getLogger(__name__)
class RequestHandler(logging.Handler):
def emit(self, record):
if record.getMessage().startswith("Hello"):
print("hello detected")
handler = RequestHandler()
logger.addHandler(handler)
logger.warning("Hello world")
Putting it all together you may be able to do something like this:
import logging
from py5paisa import FivePaisaClient
email = "myemailid#gmail.com"
pw = "mypassword"
dob = "mydateofbirth"
cred={
"APP_NAME":"app-name",
"APP_SOURCE":"app-src",
"USER_ID":"user-id",
"PASSWORD":"pw",
"USER_KEY":"user-key",
"ENCRYPTION_KEY":"enc-key"
}
client = FivePaisaClient(email=email, passwd=pw, dob=dob,cred=cred)
class PaisaClient(logging.Handler):
def __init__():
self.loggedin = False # this is the variable we can use to see if we are "logged in"
def emit(self, record):
if record.getMessage().startswith("Logged in!!")
self.loggedin = True
def login():
client.login()
logging.getLogger(py5paisa) # get the logger for the py5paisa library
# tutorial here: https://betterstack.com/community/questions/how-to-disable-logging-from-python-request-library/
logging.basicConfig(handlers=[PaisaClient()], level=0, force=True)
c = PaisaClient()
c.login()

Having trouble running multiple functions in Asyncio

I'm a novice programmer looking to build a script that reads a list of leads from Google Sheets and then messages them on telegram. I want to separate out the first and second message by three days thats why im separating the methods.
import asyncio
from telethon import TelegramClient
from telethon.errors.rpcerrorlist import SessionPasswordNeededError
import logging
from async_class import AsyncClass, AsyncObject, task, link
from sheetdata import *
logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s',
level=logging.WARNING)
api_id = id
api_hash = 'hash'
phone='phone'
username='user'
client = TelegramClient(username, api_id, api_hash)
#already been touched once
second_touch_array=[]
#touched twice
third_touch_array=[]
async def messageCount(userid):
count = 0
async for message in client.iter_messages(userid):
count+=1
yield count
async def firstMessage():
#clear prospects from array and readData from google sheet
clearProspects()
readData(sheet)
#loop through prospects and send first message
for user in prospect_array:
#check if we already messaged the prospect. If we haven't, execute function
if(messageCount(user.id) == 0):
await client.send_message(user.id, 'Hi')
second_touch_array.append(prospect(user.name, user.company, user.id))
print("First Message Sent!")
else:
print("Already messaged!")
async def secondMessage():
for user in second_touch_array:
if(messageCount(user.id) == 1):
await client.send_message(user.id, 'Hello')
third_touch_array.append(prospect(user.name, user.company, user.id))
print("Second Message Sent!")
else:
print("Prospect has already replied!")
async def main():
# Getting information about yourself
me = await client.get_me()
await firstMessage()
await secondMessage()
for user in second_touch_array:
print(user.name, user.company, user.id)
with client:
client.loop.run_until_complete(main())
Anyways, when I run my code i'm successfully getting the "Already Messaged!" print statement in my terminal from the firstMessage function.
This is good - it's detecting I've already messaged the one user on my Google Sheets list; however, my second function isn't being called at all. I'm not getting any print statement and every time I try to print the contents of the second array nothing happens.
If you have any advice it would be greatly appreciated :)

I want a way to send messages on WhatsApp using Twilio without using Sandox. Could you help me find a solution for it?

I am making an alert app for a very small company which does not have a Facebook Business Page nor a website which I could link to get the WhatsApp API. Could you please suggest me an alternative way through which I could make use of Twilio but avoid using sandbox. I have been making this project for around a year now and still not able to solve the issue successfully. Please help me solve this problem.
I am also including the code snippet below for reference.
import pandas as pd
from twilio.rest import Client
import datetime
from datetime import date, timedelta
values=variables
df=pd.DataFrame(values)
df.drop(index=df.index[0],
axis=0,
inplace=True)
df.index = df.index-1
df['AMC_Start_Date']=df['AMC_Start_Date'].apply(lambda x:x.replace('/','-'))
df['AMC_End_Date']=df['AMC_End_Date'].apply(lambda x:x.replace('/','-'))
d1=date.today()
d1=pd.to_datetime(d1)
d2=df['AMC_Start_Date']
d2=pd.to_datetime(d2)
df['AMC_Start_Date'] = pd.to_datetime(df["AMC_Start_Date"])
df['AMC_End_Date'] = pd.to_datetime(df["AMC_End_Date"])
d2=df['AMC_Start_Date']
d3=df['AMC_End_Date']
t=d1-d2
s=d1-d3
account_sid=‘’
auth_token = 'a7'
client = Client(account_sid, auth_token)
def alert2():
from datetime import date, timedelta
dt = date.today() - timedelta(856)
t2=df['AMC_Start_Date']
t2=pd.to_datetime(t2,format='%Y-%m-%d')
t3=df['AMC_End_Date']
t3=pd.to_datetime(t3,format='%Y-%m-%d')
dt1=date.today()-timedelta(989)
num=['whatsapp:+9198--------','whatsapp:+91900-------']
i1=[i for i, e in enumerate(df['AMC_Start_Date']) if e == dt]
i2=[i for i, e in enumerate(df['AMC_End_Date']) if e == dt1]
NULL=[]
for j in range(len(num)):
if i1!=NULL and i2!=NULL:
strings1 = [str(integer) for integer in i1]
a_string = "". join(strings1)
an_integer = int(a_string)
a=df['Name_and_Address'][an_integer]
strings2 = [str(integer) for integer in i2]
a_string2 = "". join(strings2)
an_integer2 = int(a_string2)
b=df['Name_and_Address'][an_integer2]
body='Servicing scheduled today for {}'.format(a)
message = client.messages.create( from_='whatsapp:+141--------', body=body, to=num[j])
body='The service ends after two days for {}'.format(b)
message = client.messages.create( from_='whatsapp:+141-------', body=body, to=num[j])
elif i1!=NULL:
strings1 = [str(integer) for integer in i1]
a_string = "". join(strings1)
an_integer = int(a_string)
a=df['Name_and_Address'][an_integer]
body='Servicing scheduled today for {}'.format(a)
message = client.messages.create( from_='whatsapp:+141-------', body=body, to=num[j])
elif i2!=NULL:
strings2 = [str(integer) for integer in i2]
a_string2 = "". join(strings2)
an_integer2 = int(a_string2)
b=df['Name_and_Address'][an_integer2]
body='The service ends after two days for {}'.format(b)
message = client.messages.create( from_='whatsapp:+141------', body=body, to=num[j])
else:
body='There are no tasks scheduled for today'
message = client.messages.create( from_='whatsapp:+141-------', body=body, to=num[j])
alert2()
Twilio developer evangelist here.
The only way to use the WhatsApp API in production with your own number and no sandbox limits is to follow the instructions in the documentation here. The instructions don't say that you need a Facebook Page or website. You do need:
a Facebook Business Manager account
to complete Facebook Business Verification
Once you have done those parts, you should start your application by filling in this form.
Without a Facebook Business Manager account, you cannot use the API. If you can't create one of those, then I do not know of a way that you can use the API.

how to get channel's members count with telegram api

I want to get a channel's members' count but I don't know which method should I use?
I am not admin in that channel, I just want to get the count number.
EDIT:I am using main telegram api, not telegram Bot api
You can use getChatMembersCount method.
Use this method to get the number of members in a chat.
It worked for me :)
from telethon import TelegramClient, sync
from telethon.tl.functions.channels import GetFullChannelRequest
api_id = API ID
api_hash = 'API HASH'
client = TelegramClient('session_name', api_id, api_hash)
client.start()
if (client.is_user_authorized() == False):
phone_number = 'PHONE NUMBER'
client.send_code_request(phone_number)
myself = client.sign_in(phone_number, input('Enter code: '))
channel = client.get_entity('CHANNEL LINK')
members = client.get_participants(channel)
print(len(members))
It is possible to do it also through GetFullChannelRequest in telethon
async def main():
async with client_to_manage as client:
full_info = await client(GetFullChannelRequest(channel="moscowproc"))
print(f"count: {full_info.full_chat.participants_count}")
if __name__ == '__main__':
client_to_manage.loop.run_until_complete(main())
or to write it without async/await
def main():
with client_to_manage as client:
full_info = client.loop.run_until_complete(client(GetFullChannelRequest(channel="moscowproc")))
print(f"count: {full_info.full_chat.participants_count}")
if __name__ == '__main__':
main()
Also as above was said, it is also feasible by bot-api with
getChatMembersCount method. You can curl it or use python to query needed url
with python code can look like this one:
import json
from urllib.request import urlopen
url ="https://api.telegram.org/bot<your-bot-api-token>/getChatMembersCount?chat_id=#<channel-name>"
with urlopen(url) as f:
resp = json.load(f)
print(resp['result'])
where <your-bot-api-token> is token provided by BotFather, and <channel-name> is channel name which amount of subscribers you want to know (of course, everything without "<>")
to check firstly, simply curl it:
curl https://api.telegram.org/bot<your-bot-api-token>/getChatMembersCount?chat_id=#<channel-name>