Threading in a irc Chat bot - python

Writing a chat bot that i think needs more then 1 thread. First Main thread reads constantly the chat. From time to time I need to check the moderators which i need to get from an URL((JSON)) and provide to the main thread so he can for example ban someone but not an moderator.
First Question: How do i make a thread run every 30 secs. I dont want to pause it. I want to run it, finish after getting the moderators list and then after some time run it again.
Code of Main File:
#Requests module, which gets JSON data from an url
#To get moderators in the chat.
def secondThread():
while(True):
getModsList()
def chatReading():
#Creates socket to the twitch irc chat
s = openSocket()
#Joins a specific chat room
joinRoom(s)
while(True):
#Revieves messages from irc
derp = s.recv(8192)
derp=derp.decode('utf-8')
temp = str.split(derp, "\n")
readbuffer = temp.pop()
#Loop which prints scanned thing from twitch irc chat
for line in temp:
#Time stamp
print(time.strftime('%X'), end=" : ")
#Pings server back
pingPong(s, line)
#This just displays the message recieved more readable User:Message
try:
print(getUser(line) + " typed :" + getMessage(line))
except (IndexError, UnicodeEncodeError):
print("Not an user")
def main():
thread_1 = threading.Thread(target=chatReading, args=[])
thread_2 = threading.Thread(target=secondThread, args=[])
thread_1.start()
thread_2.start()
if __name__ == '__main__':
main()
Second file with getModList
moderators = None
def getModsList():
gotList = False
while(gotList != True):
jsonList = requests.get(CHANNEL_GROUP)
#If the jsonList status_code == 200 that means that it got the list,
#every other number is bad.
statusFlag=jsonList.status_code
if(statusFlag == 200):
gotList = True
else:
print("Failed to get mods list, trying again")
continue
jsonList=jsonList.json()
#this gets the mods list
moderators = jsonList['chatters']['moderators']
time.sleep(10)
Second question would be more an advice. How do i plan the project? Its my first more serious project and I'm having trouble see how to plan it. I want to add later more funcionality to the bot like commands for user, mods, a raffle where a user by command can join only once, and after 5 minutes a winner is chosen. Thanks in advance.

Related

How too not clear a input when clearing the console?

When I clear the console to refresh something with
from replit import clear
def cls():
clear()
This clears the screen on a thread every 3 or so seconds but when doing that i have a input on the main thread for message inputs
def main(nick):
while True:
message = input("")
if message == "!exit":
break
else:
data = {
"message":message,
"author":nick
}
requests.post(f"{glob.serverUrl}/postMessage", json=data)
I have tried to find a fix but have not been able to find anything

Discord processing commands synchronously and replying to everyone at once. (using discord.py)

My discord bot seems to be processing all requests one after the other and replies to everyone at once when all the processing is finished, example :
user A requested to download a video
user B requested to download a video
process A's request
process B's request
reply to A and B.
Whereas, I think that it should process and reply to both the users independently.
Code:
if clean_content[0] == "!get": #Get complete video (!get link)
try:
yt = pytube.YouTube(clean_content[1])
if yt.length > time_limit:
await message.channel.send(f"Length of given video exceeds {time_limit//60} minutes, which is currently unsupported.")
return
await get_func(message, yt)
return
except:
await message.reply("Some error occured :(")
return
async def get_func(message,yt):
yt.streams.get_highest_resolution().download(output_path="./videos",filename=f"down_{message.author}")
main_folder = new_mega.find('discord_videos')
file = new_mega.upload(f"./videos/down_{message.author}", main_folder[0])
os.remove(f"./videos/down_{message.author}")
print(f"processed {message.author}")
await message.reply(f"File is only valid for {delete_time//3600} hour(s), link : {new_mega.get_upload_link(file)}")
You can use the threading when you call the get_func there you can use threading so it will run in background and when process gets done it will send msg to respective user
I managed to solve the problem.
I used the threading library and created a new thread for processing every new task, as suggested by DJ Walkzz.
New Code:
if clean_content[0] == "!get": #Get complete video (!get link)
try:
yt = pytube.YouTube(clean_content[1])
if yt.length > time_limit:
await message.channel.send(f"Length of given video exceeds {time_limit//60} minutes, which is currently unsupported.")
return
t1 = threading.Thread(target=get_func, args=(message, yt))
t1.start()
return
except:
await message.reply("Some error occured :(")
return
def get_func(message,yt):
yt.streams.get_highest_resolution().download(output_path="./videos",filename=f"down_{message.author}")
main_folder = new_mega.find('discord_videos')
file = new_mega.upload(f"./videos/down_{message.author}", main_folder[0])
os.remove(f"./videos/down_{message.author}")
print(f"processed {message.author}")
client.loop.create_task(message.reply(f"File is only valid for {delete_time//3600} hour(s), link : {new_mega.get_upload_link(file)}"))

How do I stop a loop using a command in discord?

if message.content.startswith("*run"):
ticker = message.content[5:]
try:
df = exchange.fetch_ohlcv(ticker)
await message.channel.send('Read Command: will send message if long or short opportunity arises!')
valid = True
except:
await message.channel.send('Invalid ticker')
if valid == True:
bars = exchange.fetch_ohlcv(ticker, timeframe='5m', limit=210)
df = pd.DataFrame(bars, columns=['time','high', 'low', 'open','close', 'volume'])
close = df.iloc[-1]['close']
while valid == True:
if message.content == "*stop":
valid = False
trade = strat1(ticker)
The code above is for a discord bot that a friend and I are working on for a side project. My problem is in the last few lines of the code,
while valid == True:
if message.content == "*stop":
valid = False:
When a user types in *run (ticker) in discord, the code searches through data to see whether that ticker is valid or not and then based on the response, a loop will run. During that loop, I want the user to have the option to stop it without having to turn off the bot completely, but I can't seem to get it to register the *stop command. Every time I run it, it just moves past that command completely. So, if anyone knows how to fix this, please let me know.
Thank you for reading.
What you're trying to do is to interrupt a running execution. To do it in Python, you will need to use a multiprocessing algorithm.
You can try to use the multiprocessing module,
Firstly, design a master thread that listens for command in the discord server, which you already did.
Then you would wanna put the actual service, that needs to be run after the command, in a function
def thread_worker():
bars = exchange.fetch_ohlcv(ticker, timeframe='5m', limit=210)
df = pd.DataFrame(bars, columns=['time','high', 'low', 'open','close', 'volume'])
close = df.iloc[-1]['close']
trade = strat1(ticker)
Then when a command to start is received, you can initiate a new process and run the corresponding job
newProcess = multiprocessing.Process(target=thread_worker)
newProcess.start()
When your master thread receives the kill signal
newProcess.terminate()

Running a cron scheduler within a running thread

I'm writing a telegram bot and I want to schedule it to send an automated message every day at a specific timing, cron style. I am using apscheduler to do it but I could not get the cron function to work. The interval scheduling works fine but it is not what I want.
I don't want to execute the .py file outside because I need the telegram bot to detect user's /start message.
So what I wanted to do was to detect user's /start message and then start the scheduler. Thereafter, the bot will send a message to the user everyday 8pm night.
Cron scheduling doesn't start and I don't know why. I suspect it is because it can't run within the main thread that I am running? Any advice would be helpful! Thank you.
import time
import telepot
import json
from telepot.loop import MessageLoop
from telepot.namedtuple import ReplyKeyboardMarkup # for custom keyboard
from telepot.delegate import pave_event_space, per_chat_id, create_open
from apscheduler.schedulers.blocking import BlockingScheduler
## Load token
TOKEN = 'TOKEN NUMBER'
# The main body
class main(telepot.helper.ChatHandler): # Implement continuous dialogue with user using DelegatorBot
global counter1
counter1 = 0
global counter2
counter2 = 0
def __init__(self, *args, **kwargs):
super(main, self).__init__(*args, **kwargs)
# Initialize and create empty dictionary for storing data.
self.indicator = '0'
self.data = {} # initialize an empty dictionary for storing data.
def on_chat_message(self, msg):
content_type, chat_type, chat_id = telepot.glance(msg) # this is very important.
# Debugging
print(content_type, chat_type, chat_id)
print(msg['text'])
global counter1
global counter2
scheduler = BackgroundScheduler()
#scheduler_cron = BlockingScheduler()
# Survey function
def survey():...
return
def repeat_message():
bot.sendMessage(chat_id, text='type /survey to repeat survey.')
print("Reminder sent.")
scheduler_cron.add_job(repeat_message, 'cron', day='*', week='*', day_of_week='*', hour=20, minute=00)
# Detect start messages.
while True:
if counter2 == 11: # If survey ends AKA 11 Qns done. Restart the counters.
counter1 = 0
counter2 = 0
# once bot starts, ask user to repeat survey at a specific time of the day.
if counter1 == 0: # If survey ends or at the very beginning of using the bot.
# Start message.
if msg['text'] == '/start': # /starts initiates bot. User gets greeted with a welcome message describing what the bot will do.
bot.sendMessage(chat_id,
text='Hello there.',
parse_mode='Markdown')
scheduler_cron.start()
print("Scheduler started.")
# Start survey after initializing the bot
elif msg['text'] == '/survey': # /survey initiates survey.
print("Survey started...")
#counter1 = 0
counter1 += 1
else:
bot.sendMessage(chat_id, text='I do not recognise your message.')
msg['text'] = '' # resets msg.
# User starts survey
if counter1 == 1: # if user types /survey, counter1 +=1 and when counter1 == 1, run survey function.
survey() # starts survey
counter2 += 1
break
bot = telepot.DelegatorBot(TOKEN, [pave_event_space()(per_chat_id(), create_open, main, timeout=60),])
MessageLoop(bot).run_as_thread() # Initiates the bot on telegram. Listens for user's response. If this is stopped, the entire bot stops.
print('Listening...')
while True:
time.sleep(1)
EDIT: I found out that apscheduler's cron does not work if I have another thread running in the background as stated in their documents:
BlockingScheduler: use when the scheduler is the only thing running in your process
BackgroundScheduler: use when you’re not using any of the frameworks below, and want the scheduler to run in the background inside your application
So it means I can't use apscheduler to make my bot work. Anyone know of any cron-like alternatives that allows me to schedule my telegram bot to fire message to users at specific timings of the day? Preferably, it has to be something that works with the telegram API.

Can't receive update messages from user

I am trying to create a bot that receives a response from the user and asks again if needed. The problem is that after:
update.reply_text("Did you report all you working hour on freshdesk for this week?, ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
I can't get the new updates. The message text remains /start in the first print, and the second print doesn't work at all.
How can I get correctly the response from the user? Can it be an issue related to ReplyMarkup?
def check_the_week(bot, update):
agent_username = update.message.from_user['username']
parameters = {"username": agent_username}
url = "{}/weekly_hours/".format(API_URL)
report = get_request_forwarder(url=url, method="GET", parameters=parameters)["messages"]
reply_keyboard = [['YES', 'NO']]
bot.send_message(
chat_id=update.message.chat_id,
text=report,
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) # sends the total nr of hours
print update.message.text
update.reply_text("Did you report all you working hour on freshdesk for this week?",
ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
print update.message.text
if update.message.text == "YES":
update.message.reply_text(text="Are you sure?", reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
# Asks confirmation
if update.message.text == "YES":
update.message.reply_text(text="Thank you for reporting your working hours in time!")
elif update.message.text == "NO":
update.message.reply_text(text="Please, check you time reports and add missing")
elif update.message.text == "NO":
update.message.reply_text(text="Please, check you time reports and add missing")
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(TELEGRAM_TOKEN)
j = updater.job_queue
# # Get the dispatcher to register handlers
dp = updater.dispatcher
# # Start the Bot
dp.add_handler(CommandHandler("start", check_the_week))
# Send information to manager by command
updater.start_polling()
updater.idle()
print("bot started")
if __name__ == '__main__':
main()
Because you are using a CommandHandler, which is only used to capture a single command at a time.
What you want to do can be achieved by using a ConversationHandler. Please read the example scripts here and here. Also you can read more details on the handler here.

Categories

Resources