"Run until complete" loop does not end after function is complete? - python

I have been working on a Discord bot that pulls information from Ubisoft's Rainbow Six Siege stats API. I am new to coding and I know it's a bit of a bodge, but to retrieve stats I am using some sample code I found in the API's documentation:
#types.coroutine
def run():
auth = api.Auth("my_email", "my_password")
player = yield from auth.get_player(username, api.Platforms.UPLAY)
operator = yield from player.get_operator(oprtr)
print(operator.kills)
global result
result = (operator.kills)
asyncio.get_event_loop().run_until_complete(run())
print (result)
I placed this inside of a command function I created:
#client.command()
async def wins(ctx, username, oprtr):
print (username, oprtr)
Which, together, gives the code below, which I have annotated to show how the loop doesn't end. On its own (without being inside a command), the #types.coroutine works fine, it is just when it is inside of #client.command() that the loop doesn't end.
#client.command()
async def wins(ctx, username, oprtr):
print (username, oprtr)
#types.coroutine
def run():
auth = api.Auth("my_email", "my_password")
player = yield from auth.get_player(username, api.Platforms.UPLAY)
operator = yield from player.get_operator(oprtr)
print(operator.kills) #this value prints, so the function definitely works.
global result
result = (operator.kills)
asyncio.get_event_loop().run_until_complete(run()) #this is the loop which seems to never end,
#and blocks anything from progressing
print (result) #these two outputs do not send, which tells me that everything is being
await ctx.send("Hello") #blocked by the loop
As I said, I'm a fairly new and pretty rubbish coder so I appreciate any help, this has been bugging me all day! If you need any more information I will do my best to give you it.

Instead of using a ruin until complete loop, maybe try looping until a certain variable is true or false.
notdone = true
while notdone == true:
#do whatever in here and when you want it to finish, just set notdone to false.

Related

Need telegram bot to edit a function from another file

As the title says. I need my telegram bot to take user input, and use that to change some values on another function from another file. I already got the file to be successfully run from the bot, but I can't figure out how to change values first. I am using Python-Telegram-bot.
here is the code I need to edit that is in a separate file (call.py)
call = client.calls.create(
machine_detection='Enable',
url='https://ngrok.io/main',
to='',
from_=''
)
I need to edit the "to" and "from" field(s) in this code above.
The code I use to run this from my bot is as follows:
def update(update, context):
update.message.reply_text('Enter number :\n'
'e.g. 18004585478\n')
update.message.reply_text('Calling...')
exec(open("call.py").read())
I am pretty new to all this so I know the code is not good at all. I have read that I should be using ConversationHandler or CommandHandler but I honestly am not sure how to implement it.
I edited the code based on what Alexey suggested and now am stuck on a similar issue.
def update(update, context):
update.message.reply_text('Enter number:\n'
'e.g. 18004585478\n'
'Number Must begin with 1')
from_number = update.message.text
update.message.reply_text('Enter number:\n'
'e.g. 18004585478\n'
'Number Must begin with 1')
to_number = update.message.text
update.message.reply_text('Calling...')
call_state = call.make_call(to_number, from_number)
The Telegram bot just runs all the code at once, it doesn't stop and wait for any input from the number fields. How do I go about implementing MessageHandler to make the bot stop and accept input to pass along to call_state, then execute call_state at the end?
You don't need to change the code, you need to use arguments to pass the data you wanted to.
In call.py you can make a funciton
def make_call(to_number, from_number):
call = client.calls.create(
machine_detection='Enable',
url='https://ngrok.io/main',
to=to_number,
from=from_number,
)
return call
In your update function just use the function by giving it the necessary values
import call
def update(update, context):
update.message.reply_text('Enter number :\n'
'e.g. 18004585478\n')
update.message.reply_text('Calling...')
call_state = call.make_call(to_number='0123456789', from_number='9876543210')
# use call_state ...
What Alexey stated ended up working with very slight modifications.
I took what Alexey posted and deleted the numbers and turned them into a variable I could edit from my other script.
def make_call(to_number, from_number):
call = client.calls.create(
machine_detection='Enable',
url='https:snip/main',
to=to_number,
from_=from_number
)
print(call.sid)
Then in the other file I defined the variables and executed them by importing the file I needed to edit by using user_data[FROM] = update.message.text and user_data[TO] = update.message.text.
then calling the funciton.
call_state = call.make_call({user_data[TO]}, {user_data[FROM]})
Dont forget to add user_data = {} at the top of your code.

Add commands to user input

Recently I started a project. My goal was it to have a script, which, once launched, could be able to control actions on the hosts computer if an instruction was send via email. (I wanted this so I could start tasks which take a long time to complete while I'm away from home)
I started programming and not long after I could send emails, recieve emails and analyze their content and take actions responding to the content in the email.
The way I did it was like this:
I first tried to use commands without a prefix but this caused errors so i added and "!" infront of every command which could be taken. Then I would split the contents of the email whenever there was a new line.
contents_of_the_email ="!screen\n!wait 5\n!hotkey alt tab\n"
# takes a screenshot waits 5 seconds and presses alt tab
lines = contents_of_the_email.count("\n")
contents_of_the_email = contents_of_the_email.split("\n")
for i in range(lines):
if "!screen" in contents_of_the_email[i]:
print("I took a screenshot and sent it to the specified email!")
elif "!wait " in contents_of_the_email[i]:
real = contents_of_the_email[i].split("!wait ")
print(f"I slept for {real[1]} seconds!")
elif "!hotkey " in contents_of_the_email[i]:
real = contents_of_the_email[i].split(" ")
print(f"I pressed the keys {real[1]} and {real[2]} at the same time!")
I replaced the actual code of the commands with print statements as they are not needed to understand my problem or recreate it.
Now this was getting very messy. I had added around 20 commands and they started conflicting. I mostly fixed it by changing the names of the commands but it was still spaghetti code.
I want to know if there is a more elegant way of adding commands to my program, I would desire something that works like this:
def takeScreenshot():
print("I took a screenshot!")
addNewCommand(trigger="!screen",callback=takeScreenshot())
If this is possible in python I would really appreciate to know! Thank you :D
Have you considered using dictionaries to call your functions?
def run_A():
print("A")
# code...
def run_B():
print("B")
# code...
def run_C():
print("C")
# code...
inputDict = {'a': run_A, 'b': run_B, 'c': run_C}
# After running the above
>>> inputDict['a']()
A
Try using the built-in module cmd:
import cmd, time
class ComputerControl(cmd.Cmd):
def do_screen(self, arg):
pass # Screenshot
def do_wait(self, length):
time.sleep(float(length))
def do_hotkey(self, arg):
...
inst = ComputerControl()
for line in contents_of_the_email.splitlines():
line = line.lstrip("!") # Remove the '!'
inst.onecmd(line)
You can try something like this:
def take_screenshot():
print("I took a screenshot!")
def wait(sec):
print(f"I waited for {sec} secs")
def hotkey(*args):
print(f"I pressed the keys {', '.join(args)} at the same time")
def no_operation():
print("Nothing")
FUNCTIONS = {
'!wait': wait,
'!screen': take_screenshot,
'!hotkey': hotkey,
'': no_operation
}
def call_command(command):
function, *args = command.split(' ')
FUNCTIONS[function](*args)
contents_of_the_email = "!screen\n!wait 5\n!hotkey alt tab\n"
# takes a screenshot waits 5 seconds and presses alt tab
for line in contents_of_the_email.split("\n"):
call_command(line)
Just move funtions and dict definition to separate module.

How do I make a python bot with selenium that checks when a new message has arrived from a Telegram channel?

I should start off mentioning I am pretty new to python and selenium, so I don't really know much about either of them.
what I am trying to do:
Check every second if a telegram channel I follow has sent out a new message. I have telegram web opened on a browser opened with custom flags so that I don't need to log in with telegram every single time.
So far what I did is see the class that has all the message's text inside of it
im_message_text, and get the last of them in the list.
Source Code
NOTE The "strong" tag is simply where the title is stored in side the text message, which is what i'm using to compare everything:
def getItemsInfo1(self):
try:
iteminfo1 = WebDriverWait(self.driver, 4).until(
EC.presence_of_element_located((By.CLASS_NAME, 'im_message_text')))
list3 = self.driver.find_elements_by_class_name("im_message_text")
list33 = list3[-1]
list4 = list33.find_element_by_tag_name("strong")
list4txt = list4.text
return list4txt
except:
print("Nothing is found")
What I do from here is I make another identical code which has a time.sleep(1) at the start to give the message time to appear, and i compare the 2 using the following while loop:
while Main.getItemsInfo1() == Main.getItemsInfo2():
print("No new messages. Try number ", count)
print(Main.getItemsInfo1(), Main.getItemsInfo2())
count += 1
So far, whilst as soon as there is a new message the code output highlights that the 2 methods have different text, it still does not end the while loop and it continues to say that there aren't any new messages.
Does anyone know how to help?
while Main.getItemsInfo1() == Main.getItemsInfo2():
print("No new messages. Try number ", count)
print(Main.getItemsInfo1(), Main.getItemsInfo2())
count += 1
When you call getItemsInfo1() you are calling the method again , so there is no guarantee that the value is get in print statement is same as what is there in while so use something like:
info1= Main.getItemsInfo1()
info2= Main.getItemsInfo2()
while info == Info2:
print("No new messages. Try number ", count)
info1= Main.getItemsInfo1()
info2= Main.getItemsInfo2()
print(info1,info2)
count += 1
The above code will make sure you are having same value, as you are storing the value to a variable and then validating it .
You don't even need to use telegram web or selenuim either, let's make it much more easier:
‍
from telethon import TelegramClient, events
client = TelegramClient(PHONE_NUMBER, API_ID, API_HASH)
#client.on(events.NewMessage("chanel name"))
def new_message_listener(event):
new_message = event.message.message
media_status = event.message.media
## DO what you like with new_messsage
Just remember to change the variables in the TelegramClient, You can receive API_ID, API_HASH from Telegram website.
They should be like this:
api_id = 1027347
api_hash = "c0e2cfefac982659a52da625b81e2a99"

Unsure how asyncio works and how I return information from a continuously running async folder watchdog function [duplicate]

This question already has answers here:
Yielding asyncio generator data back from event loop possible?
(3 answers)
Closed 2 years ago.
I am working on a tool that watches up to 3 folders for changes.
If a change occurs its passes the path into a function.
This function is supposed to gather information and return them in a way that I can for example use it to be displayed on a GUI.
The code is as follows.
# Contains code for different checks on the output folder of the AOIs
from export_read import aoi_output_read
import asyncio
from watchgod import awatch
import config_ini
async def __run_export_read(path):
# On every trigger of the function read a .ini with config settings
# this .ini gets changed from the outside (by hand or with a tool)
# This way we can modify the actions that happen on every read
# The config file contains the name of the functions in export_read. If the value of the key is 1 it uses getattr to run that function and return that value
# For example it runs "list_of_errors" if that key is set to 1 in the ini file and returns the result of that
config_cls = config_ini.export_read_config()
active_tools = config_cls.read_ini()
aoi_output_read_cls = aoi_output_read(path)
if not aoi_output_read_cls.is_valid(): # checks if file is valid. Returns if not
return
for tool in active_tools:
try:
method = getattr(aoi_output_read_cls, tool) # This gets the method name out of the loaded config file string
except AttributeError:
print("Class `{}` does not implement `{}`".format(aoi_output_read_cls.__class__.__name__, tool))
yield method() # "method" runs the function that got read from the ini file
# todo: I need to figure out how to keep returning data from this function as long as the async loop from the watchdog is running
async def __watchdog(path):
async for changes in awatch(path):
for items in changes:
if items[0] == 1 or items[0] == 2: # when File is created or changed
async for item in __run_export_read(items[1]):
yield item # This doesnt work as run_until_complete(__watchdog(path)) needs a different output. Which I do not understand
The yield inside __watchdog() does not work because I cant pass a list to run_until_complete. But this is as far as my knowledge goes.
def folder_watchdog(path):
loop = asyncio.get_event_loop()
loop.run_until_complete(__watchdog(path))
My main file.
import analysis
def main():
path = r"I:\Data Export\5"
print(analysis.folder_watchdog(path))
main()
Currently I just want to be able to use the print statement in main to know that that data gets passed through all of this code.
What can I do to get the data I need without stopping the execution.
Is there a completley different way of doing it?
Solved this with the following(Solution from user4815162342)
analysis.py:
def iter_over_async(ait, loop):
ait = ait.__aiter__()
async def get_next():
try:
obj = await ait.__anext__()
return False, obj
except StopAsyncIteration:
return True, None
while True:
done, obj = loop.run_until_complete(get_next())
if done:
break
yield obj
def folder_watchdog(path):
while True:
loop = asyncio.get_event_loop()
for items in iter_over_async(__watchdog(path), loop):
yield items
Now in my main file print works like this:
def main():
path = r"I:\AOI Data Export\L5"
for stuff in analysis.folder_watchdog(path):
print(stuff)
main()

asterisk ari calling stuck in ringing with python

I'm quite new with ARI scripting for Asterisk, and I've been trying to make some script to handle a 1 to 1 communication with ari-py in python. I've been following the example that provided in the asterisk wiki and so far so good. But when I try to create a call, the recipient always keep ringing, even if I have answered it. Is there something wrong with how I handle the call? Here's my script
def stasis_start_cb(self, channel, ev):
"""Handler for StasisStart event"""
chan = channel.get('channel')
chan.answer()
print "Channel %s has entered the application" % chan.json.get('name')
outgoing = client.channels.originate(endpoint="SIP/1002", extension='1002', callerId='Tes', app='channel-dump', appArgs='dialed')
I tried using OOP to simplify the function usage, are there anything wrong with that script? And here's another script trying to make a call by using a bridge:
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
def outgoing_start(self, bri, channel):
channel.answer()
self.addChan(channel, bridge)
def stasis_start(self, channel, ev):
chan = channel.get('channel')
name = chan.json.get('name')
"""ars = ev.get('args')
if not ars:
print "Error: {} didn't provide any arguments!".format(name)
return
if ars and ars[0] != 'inbound':
return
if len(ars) != 2:
print "Error: {} didn't tell us who to dial".format(name)
chan.hangup()"""
print "Channel {} entered the application".format(name)
chan.ring()
self.outgoing_call(chan)
self.outgoing_start(bridge, chan)
Both the client is able to be added in the bridge, and I can make a call, but the problem still persist, the recipient keep saying they are ringing despite I have answered the call
Turns out, the problem is in here
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
As the dialed number answer the call, they uses the same script, so they ended up calling themselves again. A simple if condition to make the dialed number not call to itself again is all that is needed

Categories

Resources