This is the first script which get's data from a website:
import requests
def get_prices():
name = "SeedifyFund"
crypto_data = requests.get("https://api.pancakeswap.info/api/tokens").json()["data"]
data = None
for i in crypto_data:
current = crypto_data[i]
if current['name'] == name:
data = {
"PriceUSD": current["price"],
"PriceBNB": current["price_BNB"],
}
return data
if __name__ == "__main__":
print(get_prices())
The code above outputs the following: {'PriceUSD': '1.022239219137518991087869433174527', 'PriceBNB': '0.002452203037583603303073246037795846'}
I'm having issue an issue with the second script. I want it to use the data that it has collected above and print it in a telegram bot when the user types /price. The code for the second script:
import telegram
from telegram.ext import Updater
from telegram.ext import CommandHandler
from tracker import get_prices
telegram_bot_token = "API TOKEN"
updater = Updater(token=telegram_bot_token, use_context=True)
dispatcher = updater.dispatcher
def price(update, context):
chat_id = update.effective_chat.id
message = ""
crypto_data = get_prices()
for i in crypto_data:
bnbprice = crypto_data[i]["pricebnb"]
usdprice = crypto_data[i]["priceusd"]
message += f"1 SFUND = \n${usdprice:,.2f} USD\n{bnbprice:.3f} BNB\n\n"
context.bot.send_message(chat_id=chat_id, text=message)
dispatcher.add_handler(CommandHandler("price", price))
updater.start_polling()
When the user types /price in the telegram chat it give this error:
coin = crypto_data[i]["pricebnb"]
TypeError: string indices must be integers
Could someone tell me what I'm doing wrong and help me solve the issue. Many thanks
Related
I'm trying to create a python telegram bot that send random news every x time, and it works when i give the method news() the chat_id statically directly on the method, I tried passing it directly on the method start(), that execute a run_repeating job like that:
def start():
context.job_queue.run_repeating(news(update=update, context=context), interval=newsTimer, first=1)
but after you run the command for the first time (and works) it trows a lot of errors.
Instead I hardcoded the chat_id inside the news() method.
Anyone has any hint on how could I pass the variable update to the news() method?
Here's my full code:
from dotenv import load_dotenv
import telegram.ext
import requests
import datetime
import random
import json
import os
load_dotenv()
token = os.getenv('TOKEN')
api = os.getenv('API')
languages = "en"
newsTimer = 600
query = "world"
pageSize = "100"
yesterday = str(datetime.datetime.now() - datetime.timedelta(days=1))
today = str(datetime.datetime.now())
api_url = "https://newsapi.org/v2/everything?q=" + query + "&from=" + yesterday + "&to=" + today + "&sortBy=popularity&pageSize=+"+ pageSize +"&apiKey=" + api
def start(update, context):
context.job_queue.run_repeating(news, interval=newsTimer, first=1)
def help(update, context):
update.message.reply_text("""
Available commands:
/start - start bot
/help - help
""")
def news(context):
response = requests.get(api_url)
json_data = json.loads(response.text)
i = random.randint(0, 99)
title = json_data['articles'][i]['title']
image = json_data['articles'][i]['urlToImage']
description = json_data['articles'][i]['description']
author = json_data['articles'][i]['author']
article = json_data['articles'][i]
if title is None:
title = "No title"
if image is None:
image = "https://www.alfasolare.ru/a_solar_restyle/wp-content/themes/consultix/images/no-image-found-360x260.png"
if description is None:
description = "No Content"
if author is None:
author = "No Author"
if article is None:
i = random.randint(0, len(json_data['articles']))
caption = "<a>" + title + "\n" + description + "\n Author: " + author + "</a>"
chat_id = "XXXXXXXXX" #my own chat id
context.bot.send_photo(
chat_id=chat_id,
photo=image,
caption=caption,
parse_mode=telegram.ParseMode.HTML
)
if __name__ == '__main__':
updater = telegram.ext.Updater(token, use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(telegram.ext.CommandHandler('start', start))
dispatcher.add_handler(telegram.ext.CommandHandler('help', help))
if updater.start_polling():
print("Bot started successfully!")
if updater.idle():
print("Bot stopped")
context.job_queue.run_repeating(news(update=update, context=context), interval=newsTimer, first=1)
this can't work since the callback argument of JobQueue.run_* must be a function. However, news(update=update, context=context) is the return value of that function call, which is None as news doesn't return anything.
To pass additional data to the job callback, you can use the context argument of JobQueue.run_*. This is also explained in PTBs wiki page on JobQueue and showcased in the timerbot.py example.
Disclaimer: I'm currently the maintainer of python-telegram-bot
I'm using peewee to store participants in a telegram channel. How do I get only new participants, i.e. those who have not been previously added?
Maybe we can offset by time? or offset by those who are already in the database?
Not so sure how to perform offsets in GetParticipantsRequest
from telethon import TelegramClient
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from time import sleep
from schema import channel_users as cudb
from datetime import datetime
import json
from dotenv import load_dotenv
load_dotenv()
import os
api_id = os.getenv('API_ID')
api_hash = os.getenv('API_HASH')
PHONE = os.getenv('PHONE')
USERNAME = os.getenv('USERNAME')
# Remember to use your own values from my.telegram.org!
client = TelegramClient('anon', api_id, api_hash)
async def main():
# Getting information about yourself
me = await client.get_me()
my_channel = 'https://t.me/some_channel_url'
offset = 0
limit = 100
all_participants = []
while True:
participants = await client(GetParticipantsRequest(
my_channel, ChannelParticipantsSearch(''), offset, limit,
hash=0
))
if not participants.users:
break
all_participants.extend(participants.users)
offset += len(participants.users)
all_user_details = []
for participant in all_participants:
now = datetime.now()
date_added = now.strftime("%d/%m/%Y, %H:%M:%S")
channel_user_id, created = cudb.get_or_create(
id = participant.id,
defaults = {'first_name' : participant.first_name,
'last_name' : participant.last_name,
'username' : participant.username,
'phone' : participant.phone,
'is_bot' : participant.bot,
'date_added' : date_added}
)
if (created):
print(f'successfully created channel_usersID = {channel_user_id}')
else:
print(f'did not create anything, user information found in channel_usersID {channel_user_id}')
with client:
client.loop.run_until_complete(main())
ok I've sort of solved it with this. Problem is - Now trying to figure out how to update every time new user joins
while True:
participants = await client(GetParticipantsRequest(
my_channel, ChannelParticipantsSearch(''), offset, limit,
hash=0
))
number_of_participants = len(participants.users)
print(f'{len(participants.users)} length')
max_cudb = cudb.select(fn.MAX(cudb.channel_usersID)).scalar()
if max_cudb == len(participants.users):
print('id is same as number of participants in group, hence nothing new')
break
if not participants.users:
break
# calculate the difference between number of participants and last user added to DB
number_to_add = number_of_participants - max_cudb
# adds missing users chronologically from oldest to most recent
print(f'number_to_add = {number_to_add}')
for i in range(number_to_add-1,-1,-1):
print(f'i = {i}')
participant = participants.users[i]
now = datetime.now()
date_added = now.strftime("%d/%m/%Y, %H:%M:%S")
channel_user_id, created = cudb.get_or_create(
id = participant.id,
defaults = {'first_name' : participant.first_name,
'last_name' : participant.last_name,
'username' : participant.username,
'phone' : participant.phone,
'is_bot' : participant.bot,
'date_added' : date_added}
)
# Prints status of DB addition
if (created):
print(f'successfully created channel_usersID = {channel_user_id}')
else:
print(f'did not create anything, user information found in channel_usersID {channel_user_id}')
https://docs.telethon.dev/en/stable/quick-references/events-reference.html?highlight=chataction#chataction here you are the docs for chataction, exactly what you need just make sure to filter the event.
I'm new to python and i'm making discord bot. So here i have twitch notification function, but when someone is live bot just starts spamming, i think because idk how to get content out of an embed. please help me. the code:
import os
import json
import discord
import requests
from discord.ext import tasks, commands
from discord.utils import get
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)
TOKEN = os.getenv('token')
# Authentication with Twitch API.
client_id = os.getenv('client_id')
client_secret = os.getenv('Dweller_token')
body = {
'client_id': client_id,
'client_secret': client_secret,
"grant_type": 'client_credentials'
}
r = requests.post('https://id.twitch.tv/oauth2/token', body)
keys = r.json()
headers = {
'Client-ID': client_id,
'Authorization': 'Bearer ' + keys['access_token']
}
'''user_info = twitch.get_users(logins=['turb4ik'])
user_id = user_info['data'][0]['id']
print(user_info)'''
# Returns true if online, false if not.
def checkuser(streamer_name):
stream = requests.get('https://api.twitch.tv/helix/streams?user_login=' + streamer_name, headers=headers)
stream_data = stream.json()
if len(stream_data['data']) == 1:
return True, stream_data
else:
return False, stream_data
# Executes when bot is started
#bot.event
async def on_ready():
# Defines a loop that will run every 10 seconds (checks for live users every 10 seconds).
#tasks.loop(seconds=10)
async def live_notifs_loop():
# username = stream_data['data'][0]['user_name']
# stream_title = stream_data['data'][0]['title']
# game_being_played = stream_data['data'][0]['game_name']
# Opens and reads the json file
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Makes sure the json isn't empty before continuing.
if streamers is not None:
# Gets the guild, 'twitch streams' channel, and streaming role.
guild = bot.get_guild(690995360411156531)
channel = bot.get_channel(798127930295058442)
role = get(guild.roles, id=835581408272580649)
# Loops through the json and gets the key,value which in this case is the user_id and twitch_name of
# every item in the json.
for user_id, twitch_name in streamers.items():
print("checking" + " " + str(twitch_name))
# Takes the given twitch_name and checks it using the checkuser function to see if they're live.
# Returns either true or false.
status, stream_data = checkuser(twitch_name)
# Gets the user using the collected user_id in the json
user = bot.get_user(int(user_id))
# Makes sure they're live
if status is True:
# Checks to see if the live message has already been sent.
async for message in channel.history(limit=200):
print("yes")
twitch_embed = discord.Embed(
title=f":red_circle: **LIVE**\n{user.name} is now streaming on Twitch! \n \n {stream_data['data'][0]['title']}",
color=0xac1efb,
url=f'\nhttps://www.twitch.tv/{twitch_name}'
)
twitch_embed.add_field(
name = '**Game**',
value = stream_data['data'][0]['game_name'],
inline = True
)
twitch_embed.add_field(
name = '**Viewers**',
value = stream_data['data'][0]['viewer_count'],
inline = True
)
twitch_embed.set_author(
name = str(twitch_name),
icon_url = stream_data['data'][0]['thumbnail_url']
)
twitch_embed.set_image(url = f'https://www.twitch.tv/{twitch_name}')
print("yes2")
try:
embed_title = twitch_embed.title
embed_description = twitch_embed.description
except Exception as e:
break
print("yes3")
# If it has, break the loop (do nothing).
if embed_title == True:
break
# If it hasn't, assign them the streaming role and send the message.
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and
# they're live, give them the streaming role.
if member.id == int(user_id):
await member.add_roles(role)
# Sends the live notification to the 'twitch streams' channel then breaks the loop.
await channel.send(
content = f"Hey #everyone! {user.name} is now streaming on Twitch! Go check it out: https://www.twitch.tv/{twitch_name}", embed=twitch_embed)
print(f"{user} started streaming. Sending a notification.")
break
# If they aren't live do this:
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and they're not
# live, remove the streaming role.
if member.id == int(user_id):
await member.remove_roles(role)
# Checks to see if the live notification was sent.
async for message in channel.history(limit=200):
try:
embed_title = message.embeds[0].title
embed_description = message.embeds[0].description
except Exception as e:
break
# If it was, delete it.
if user.mention in embed_title and "is now playing" in embed_title:
print(f"{user} stopped streaming. Removing the notification.")
await message.delete()
# Start your loop.
live_notifs_loop.start()
# Command to add Twitch usernames to the json.
#bot.command(name='addtwitch', help='Adds your Twitch to the live notifs.', pass_context=True)
async def add_twitch(ctx, twitch_name):
# Opens and reads the json file.
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Gets the users id that called the command.
user_id = ctx.author.id
# Assigns their given twitch_name to their discord id and adds it to the streamers.json.
streamers[user_id] = twitch_name
# Adds the changes we made to the json file.
with open('streamers.json', 'w') as file:
file.write(json.dumps(streamers))
# Tells the user it worked.
await ctx.send(f"Added {twitch_name} for {ctx.author} to the notifications list.")
print('Server Running')
bot.run(TOKEN)
Just for others context, he had shown me the error on discord, so I am answering him through this question!
So mate, I found the error.
The line 106 in your code is:
await message.channel.send(msg)
Now this msg variable send all the details but we just want the content of it and not anything else.
So change that to:
await message.channel.send(msg.content)
Thank You! :D
When trying to sendMessage it tells me the chat id is not defined. I am able to answerCallbackQuery because it needs query ID not chat.
If I try to enter in 'chat_id' in the DEF on callback query area it throws up more errors
Where exactly in the code do I need to define it?
import sys
import time
import os
import telepot
from telepot.loop import MessageLoop
from telepot.namedtuple import InlineKeyboardMarkup, InlineKeyboardButton
def on_chat_message(msg):
content_type, chat_type, chat_id = telepot.glance(msg)
#creating buttons
if content_type == 'text':
if msg['text'] == '/start':
bot.sendMessage(chat_id, 'Welcome to #UK_Cali Teleshop\n Created by JonSnow 2021',reply_markup = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="Feedback",callback_data='a'), InlineKeyboardButton(text="You",callback_data='b'),InlineKeyboardButton(text="PGP",callback_data='c'), InlineKeyboardButton(text="Cunt",callback_data='d')],
[InlineKeyboardButton(text="Products",callback_data='e')]
]
))
def on_callback_query(msg):
query_id, from_id, query_data = telepot.glance(msg, flavor='callback_query')
print('Callback Query:', query_id, from_id, query_data)
#find callback data
if query_data == 'a':
#bot.sendMessage(chat_id, 'dsuhsdd')
#answerCallbackQuery puts the message at top
bot.answerCallbackQuery(query_id, 'products')
bot = telepot.Bot('1646167995:AAGsOwfjcryYYkoah69QJ6XGA7koUywmuRk')
MessageLoop(bot, {'chat': on_chat_message,
'callback_query': on_callback_query}).run_as_thread()
print('Listening ...')
while 1:
time.sleep(10)
The chat_id variable is local to on_chat_message. on_callback_query does not have access to it. The most Pythonic method would make these into a class and store the chat id in a member variable, but you can do it here by adding
global chat_id
as the first line of on_chat_message. You don't need that in on_callback_query because you aren't changing the value, although it won't hurt.
I have this function which works fine locally on my machine with python 3.8, but it throws runtime error on Google Cloud Functions.
def telegram_test(request):
request_json = request.get_json()
import datetime
import pandas as pd
from pyrogram import Client
session_string = "...............38Q8uTHG5gHwyWD8nW6h................."
# the rest of the authantication
api_id = 32494131641215
api_hash = "ioadsfsjnjksfgnfriuthg#qw]/zwq ]w/\lc ec,"
# one of bbc channels on telegram you want to access
channel_name = 'pyrogram'
# if you only want to get messages older than 7 days in unix style
seven_days = int((datetime.datetime.now() - datetime.timedelta(days=7)).timestamp())
# call telegram with parameters such as limit and date
# save the result to dataframe
with Client(session_string,api_id,api_hash, takeout=True,workers=2) as app:
hist_iter = app.iter_history(channel_name,offset_date=seven_days, limit=100)
msglist = [msg.__dict__ for msg in hist_iter]
df = pd.DataFrame(msglist)
print(df.head(5))
return f'it works!:{request_json}'
The error message I get from GCF log:
File "/opt/python3.8/lib/python3.8/asyncio/events.py", line 639, in
get_event_loop raise RuntimeError('There is no current event loop in
thread %r.' RuntimeError: There is no current event loop in thread
'ThreadPoolExecutor-0_0'.
Update
I updated the code, the runtime error gone. but I am getting time out error.
I put the timeout 180 secondes, but still when I test the function times out on 60 seconds.
Here is the updated code. Is there something I am doing wrong?
async def foo():
from datetime import datetime, timedelta
from pandas import DataFrame
from pyrogram import Client
import asyncio
session_string = "********zNmkubA4ibjsdjhsdfjlhweruifnjkldfioY5DE*********"
api_id = 325511548224831351
api_hash = "jdffjgtrkjhfklmrtgjtrm;sesews;;wex"
channel_name = 'cnn'
with Client(session_string, api_id, api_hash, takeout=True) as app:
hist_iter = app.iter_history(
channel_name, limit=10)
msglist = [msg.__dict__ for msg in hist_iter]
df = DataFrame(msglist)
return df
async def bar():
return await foo()
def test(request):
from asyncio import run
return run(bar())
The solution in the end was to change from Pyrogram to telethon and create the asyncio manaually before creating the client.
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
Note: you need valid session string, otherwise when you test the function, it will wait for you to auth with mobile number. so first run this code locally and authenticate, then copy the session string to the cloud function.
Here is the full code:
from telethon.sessions import StringSession
from telethon import TelegramClient
from pandas import DataFrame
import datetime
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
api_id = 101010101
api_hash = "jhafcgahagfbahgdbw17171736456gerf"
session_string = "hjksdhjbdsfhgbdsabeyitrgdsbfsdbdiyfhsbddasbdjdksf="
channel_name = 'bbcuzbek'
seven_days = int((datetime.datetime.now() -
datetime.timedelta(days=7)).timestamp())
client = TelegramClient(StringSession(session_string),
api_id, api_hash, loop=loop)
time_format = "%d/%m/%Y, %H:%M:%S"
download_date = datetime.datetime.now(
tz=datetime.timezone.utc).strftime(time_format)
cols = ["id", "date", "text", "views", "download_date"]
async def foo():
all_msgs = [[message.id, message.date.strftime(time_format), message.text, message.views, download_date] async for message in client.iter_messages(entity=channel_name, offset_date=seven_days, limit=10)]
df = DataFrame(data=all_msgs, columns=cols)
# write it to BQ
# print(df)
# async for message in client.iter_messages(entity=channel_name, offset_date=seven_days, limit=10):
# print(message.id, message.date, message.text, message.views)
print("it runs")
print(len(df))
return None
def test(request):
with client:
return client.loop.run_until_complete(foo())
bar() is redundant
You're trying to return a dataframe. Is it a valid HTTP response?
with -> async with
hist_iter = app.iter_history() -> hist_iter = await app.iter_history()
M.b. it waits for input?