Add commands to user input - python

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.

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.

Locust/Python: Splitting a tasks array with if conditions in a SequentialTaskSet

I'm new to Locust, and Python in general. I've been using JMeter for several years, and I'm trying to implement similar logic to what I've used there for handling failures in login.
I want to run a simple scenario: Login, Enter a folder, Logout
Where I'm having trouble is implementing this logic:
If any login call fails, I don't want to attempt the folder enter (avoid a cascading failure after login), but I still want to run the logout to ensure there is no active session.
My current SequentialTaskSet reads like this, which works for executing the child tasksets:
class folder_enter_scalability_taskset(SequentialTaskSet):
def on_start(self):
self.tm = TransactionManager()
#task
def seedata_config(self):
seeddata = next(seeddata_reader)
self.client.username = seeddata['username']
self.client.password = seeddata['password']
tasks = [login_taskset, folder_enter_taskset, logout_taskset]
Is there a way to split up the tasks array into multiple steps within the same SequentialTaskSet?
Always Login
if login passed, enter folder
Always Logout
Thanks to phil in the comments for pointing me in the right direction, I now have a working code with try-finally and if-else instead of a tasks array. Hope this can help someone else:
class folder_enter_scalability_taskset(SequentialTaskSet):
def on_start(self):
self.tm = TransactionManager()
#task
def seedata_config(self):
seeddata = next(seeddata_reader)
self.client.username = seeddata['username']
self.client.password = seeddata['password']
self.client.userrole = seeddata['userrole']
#task
def folder_enter_scalability(self):
try:
login_taskset.login(self)
if self.client.loginFail is False:
folder_enter_taskset.folder_enter(self)
else:
logging.error("Login failed, skipping to logout")
finally:
logout_taskset.logout(self)
I wasn't having luck with getting it to skip folder enter if I placed it in a try-else-finally condition, so I went with adding this self.client.loginFail in the login taskset to support if-else within try.
It's initiated as 'False', and flipped to 'True' in case of failure in the taskset.

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

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.

How to display code in Streamlit based on user answer?

I'm trying to create a tutorial for a library with Streamlit. My overall idea ist to walk through the different functions and classes and explain them together with user based Input, so everything becomes a litte bit more understandable for beginners.
However, I've written 5 Tutorials previously for more experienced users and would like to reuse some of that code by calling it from within my app and to only maintain it once.
Additionally, I'm walking through a lot of functions and classes, example config files e.g. and I'm calling them from a dict.
As Streamlit offers with st.echo an Option to run code and and then display it I've tried this. Also I've tried to use the python inspect Element together with st.write. However, st.echo simply displays the function name, and st.write together with inspect simply displays a string.
display_code = st.radio("Would you like to display the code?", ("Yes", "No"))
if display_code == "Yes":
with st.echo():
example_function_1()
else:
example_function_1()
Basically I'm looking for an option to pass a function and based on user Input simply run it or run it and display the code and comments to it.
So if the user selected "Yes", the Output would be, while also x,y are returned.
def example_function_1():
"""
This is and example functions that is now displayed.
"""
Some Magic
return x, y
And if the user selected No, then only x,y are returned
Here's a modified version of Streamlit's st.echo() that includes a checkbox:
import contextlib
import textwrap
import traceback
import streamlit as st
from streamlit import source_util
#contextlib.contextmanager
def maybe_echo():
if not st.checkbox("Show Code"):
yield
return
code = st.empty()
try:
frame = traceback.extract_stack()[-3]
filename, start_line = frame.filename, frame.lineno
yield
frame = traceback.extract_stack()[-3]
end_line = frame.lineno
lines_to_display = []
with source_util.open_python_file(filename) as source_file:
source_lines = source_file.readlines()
lines_to_display.extend(source_lines[start_line:end_line])
initial_spaces = st._SPACES_RE.match(lines_to_display[0]).end()
for line in source_lines[end_line:]:
indentation = st._SPACES_RE.match(line).end()
# The != 1 is because we want to allow '\n' between sections.
if indentation != 1 and indentation < initial_spaces:
break
lines_to_display.append(line)
lines_to_display = textwrap.dedent("".join(lines_to_display))
code.code(lines_to_display, "python")
except FileNotFoundError as err:
code.warning("Unable to display code. %s" % err)
You can use it exactly as you'd use st.echo. For example:
with maybe_echo():
some_computation = "Hello, world!"
st.write(some_computation)
You can use session state to pass on user input into on-screen actions. A clear example with radio buttons can be found here. Generally speaking, you need to use st.write() to accomplish this. A simplified example with a slider:
import streamlit as st
x = st.slider('Select a value')
st.write(x, 'squared is', x * x)
What you are looking for is not exactly possible, since you have to specify the function within the with st.echo() block. You can see it here:
import inspect
import streamlit as st
radio = st.radio(label="", options=["Yes", "No"])
if radio == "Yes":
with st.echo():
def another_function():
pass
# Print and execute function
another_function()
elif radio == "No":
# another_function is out of scope here..
another_function()
For this task you can use st.code().
import streamlit as st
with open("test.py") as f:
lines_to_display = f.read()
st.code(lines_to_display, "python")

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