How do I use Tkinter and Telebot together? - python

I've been working on a Python project where I'm stuck at one point. Basically, I have a GUI that updates automatically once 5 seconds. I have done it using the method .after(5000, main) where main function looks like the following.
def main():
global lastthreshold
global run
global lastaction
global bakiye
global lif
global gsecenek
paributl, binancedolar, paribudolar, dolar = getData()
farklow, farkhigh = diff(paributl, binancedolar, paribudolar)
data = guiDataMaker(paributl, binancedolar, paribudolar, farklow, farkhigh)
gui = Gui(data, dolar, lastthreshold, alsatmiktar, bakiye)
#print(*data, sep="\n")
#print(*cooldown.items(), sep="\n")
if run:
lif = gui.gui()
for i in paributl.keys():
cooldown[i] = "NONE"
run = False
gui.secenek.set(gsecenek)
runbot = runBot(lastthreshold, bakiye, data, cooldown, alsatmiktar)
if runbot[0] != None:
currency, guncelfark, fiyat, newbakiye, buydate, islem = runbot
bakiye = newbakiye
guncelislem = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
lastaction = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
gui.updateGuiData(lif, cooldown, guncelislem)
else:
gui.updateGuiData(lif, cooldown, lastaction)
src.after(5000, main)
src.mainloop()
main()
#src.mainloop()
#bot.polling()
My actual code is something around 450 lines, so I think pasting it here would be a waste of space. Though, I can do that if it helps you help me more easily. Anyway, the above code works perfectly with a single thread which is created by src.mainloop().
In the project, what I'm trying to do is basically a trading bot. To help the user see what is going on, I created a gui using Tkinter library. I create the gui only once and I only update the data every 5 seconds.
What I want to achieve is to create a Telegram Bot which the user can interact with. For example use should be able to type /BTC-USD to get information about bitcoin-dolar. So far, I achieved to send the user every buy/sell operation of the bot in telegram. However, what I'm stuck right now is to get commands of the user since it requires me to use bot.polling() which is another thread. Wherever I put this line of code in my code, it causes GUI to "not responding".
So in simpler words, my question is how can I have a Telegram Bot interacting with a user by sending messages and getting commands, also a GUI that is updated automatically every 5 seconds?

Related

Is it possible to make the cooldown time only last while a command is running in discord.py?

I'm developing a discord bot, and one of it's features is a command that feeds memes, it's only available for some channels I'm the server, and I wanted to know if it's possible to put the command in cooldown while it's being used, then a new feed couldn't be started while there's already one feed being executed, also I wanted this to only happen at a channel, for example: A user used the feed command in channel-1, of him or someone try to start a new feed before the current one ends it will fail, but if another feed is started at channel-2 it should work.
I don't know if it's possible, but if yes, how should it be done? I'm currently using #commands.cooldown decorator to put a normal cooldown on the command.
So I think the way to do it is without using the #commands.cooldown decorator and some custom way of doing the cooldown.
# use datetime
import datetime
# define this somewhere
meme_feed_cooldowns = {}
.
.
.
#client.command(description="feed memes")
async def meme_feed(ctx):
now = datetime.datetime.now()
channel_id = ctx.channel.id
previous_time_triggered = meme_feed_cooldowns.get(channel_id, None)
if previous_time_triggered:
# check if we're in cooldown
time_since_triggered = now - previous_time_triggered
if time_since_triggered.total_seconds() < 1800:
# it's not been 30 minutes since last triggered
# send some kind of message to inform the user and then return
return
# set the cooldown time to the current time
meme_feed_cooldowns[channel_id] = now
# do whatever you want to do
Something kind of like this. There's other improvements you could make - make the "cooldown dict" more complicated and also have whether or not the command is ongoing. Hopefully gives you enough ideas to get you going!

Python How do i fix Tkinter window crashing due to sleep function

I have a problem with my Tkinter window crashing due to the usage of the 'sleep' function in the code below.
The programm in the background works just fine, even if the window crashed a long time ago.
How can I use functions like time.time to ensure that I don't make queries to the API too frequently rather than making a blocking call to time.sleep.
In other words, once I follow a list of new users, how do I prevent the program from making another request in the next 30 seconds?
def follow_users(self,users_list):
api = self.api
api.login()
api.getSelfUsersFollowing()
result = api.LastJson
for user in result['users']:
following_users.append(user['pk'])
for user in users_list:
if not user['pk'] in following_users:
print('Following #' + user['username'])
api.follow(user['pk'])
# set this really long to avoid from suspension
sleep(30)
else:
print('Already following #' + user['username'])
sleep(15)
def unfollow_users(self):
api = self.api
api.login()
api.getSelfUserFollowers()
result = api.LastJson
for user in result['users']:
follower_users.append({'pk':user['pk'], 'username':user['username']})
api.getSelfUsersFollowing()
result = api.LastJson
for user in result['users']:
following_users.append({'pk':user['pk'],'username':user['username']})
for user in following_users:
if not user['pk'] in follower_users:
print('Unfollowing #' + user['username'])
api.unfollow(user['pk'])
sleep(20)
# set this really long to avoid from suspension
I would start by writing a function that follows a single user, and nothing more. It doesn't need to loop over a list or sleep or anything like that, it should just follow that one user.
For example:
def follow_user(self, user):
... code to follow this user ...
Next, define a function that pulls one user off of a list, calls the follow_user function, and then re-schedules itself to run again after a timeout.
The following example assumes there's a global variable named root which represents the root window, but you can use any widget you want. It also assumes that the class maintains a list of users to be followed in an instance variable named self.users.
def follow_users(self):
if self.users:
user = self.users.pop()
self.follow_user(user)
root.after(30000, self.follow_users)
Then, once your program starts, call this function exactly once. It will call itself once every 30 seconds. If there is at least one user in self.users it will pull it from the list and follow the user. 30 seconds later it will do it again, and then again, and so on. You can update self.users whenever you want and that user will eventually get followed.

Discord Rich presence

So basically I'm trying to have my account on discord online 24/7 so I wrote a bit code for that in python, now I want to add a rich presence, with a timer (how much time elapsed since I started the game) a game title, a large image key and all of that stuff and I have no idea how to write it, anyone knows how to help?
My code so far in main.py...
from discord.ext import tasks, commands
client = commands.Bot(
command_prefix=':',
self_bot=True
)
game = discord.Game("Game Title")
#client.event
async def on_connect():
await client.change_presence(status=discord.Status.online, activity=game)
keep_alive.keep_alive()
client.run(os.getenv("TOKEN"), bot=False)```
Using self bots is actually against the terms of service. Instead of using discord.py use pypresense You can do a lot more with it.
This is an example of time elapsed
from pypresence import Presence
import time
"""
You need to upload your image(s) here:
https://discordapp.com/developers/applications/<APP ID>/rich-presence/assets
"""
client_id = "client_id" # Enter your Application ID here.
RPC = Presence(client_id=client_id)
RPC.connect()
# Make sure you are using the same name that you used when uploading the image
start_time=time.time() # Using the time that we imported at the start. start_time equals time.
RPC.update(large_image="LARGE_IMAGE_HERE", large_text="Programming B)",
small_image="SMALL_IMAGE_HERE", small_text="Hello!", start=start_time) # We want to apply start time when you run the presence.
while 1:
time.sleep(15) #Can only update presence every 15 seconds
You can install it using pip install pypresence
I know this is an old question, and I am sorry if this is not what you are looking for, but discord.py is not made for user-bots (atleast not intended), I recommend using something like lewdneko's pypresence. It has bizzare compatibility/detection (Linux, especially Arch) issues but when it works, it works very well actually. It also has an attribute for time_elapsed, which you can use with something like time.time(). (afaik)

How to transfer a value from a function in one script to another script without re-running the function (python)?

I'm really new to programming in general and very inexperienced, and I'm learning python as I think it's more simple than other languages. Anyway, I'm trying to use Flask-Ask with ngrok to program an Alexa skill to check data online (which changes a couple of times per hour). The script takes four different numbers (from a different URL) and organizes it into a dictionary, and uses Selenium and phantomjs to access the data.
Obviously, this exceeds the 8-10 second maximum runtime for an intent before Alexa decides that it's taken too long and returns an error message (I know its timing out as ngrok and the python log would show if an actual error occurred, and it invariably occurs after 8-10 seconds even though after 8-10 seconds it should be in the middle of the script). I've read that I could just reprompt it, but I don't know how and that would only give me 8-10 more seconds, and the script usually takes about 25 seconds just to get the data from the internet (and then maybe a second to turn it into a dictionary).
I tried putting the getData function right after the intent that runs when the Alexa skill is first invoked, but it only runs when I initialize my local server and just holds the data for every new Alexa session. Because the data changes frequently, I want it to perform the function every time I start a new session for the skill with Alexa.
So, I decided just to outsource the function that actually gets the data to another script, and make that other script run constantly in a loop. Here's the code I used.
import time
def getData():
username = '' #username hidden for anonymity
password = '' #password hidden for anonymity
browser = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
browser.get("https://gradebook.com") #actual website name changed
browser.find_element_by_name("username").clear()
browser.find_element_by_name("username").send_keys(username)
browser.find_element_by_name("password").clear()
browser.find_element_by_name("password").send_keys(password)
browser.find_element_by_name("password").send_keys(Keys.RETURN)
global currentgrades
currentgrades = []
gradeids = ['2018202', '2018185', '2018223', '2018626', '2018473', '2018871', '2018886']
for x in range(0, len(gradeids)):
try:
gradeurl = "https://www.gradebook.com/grades/"
browser.get(gradeurl)
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:3]
if grade[2] != "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:4]
if grade[1] == "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:1]
currentgrades.append(grade)
except Exception:
currentgrades.append('No assignments found')
continue
dictionary = {"class1": currentgrades[0], "class2": currentgrades[1], "class3": currentgrades[2], "class4": currentgrades[3], "class5": currentgrades[4], "class6": currentgrades[5], "class7": currentgrades[6]}
return dictionary
def run():
dictionary = getData()
time.sleep(60)
That script runs constantly and does what I want, but then in my other script, I don't know how to just call the dictionary variable. When I use
from getdata.py import dictionary
in the Flask-ask script it just runs the loop and constantly gets the data. I just want the Flask-ask script to take the variable defined in the "run" function and then use it without running any of the actual scripts defined in the getdata script, which have already run and gotten the correct data. If it matters, both scripts are running in Terminal on a MacBook.
Is there any way to do what I'm asking about, or are there any easier workarounds? Any and all help is appreciated!
It sounds like you want to import the function, so you can run it; rather than importing the dictionary.
try deleting the run function and then in your other script
from getdata import getData
Then each time you write getData() it will run your code and get a new up-to-date dictionary.
Is this what you were asking about?
This issue has been resolved.
As for the original question, I didn't figure out how to make it just import the dictionary instead of first running the function to generate the dictionary. Furthermore, I realized there had to be a more practical solution than constantly running a script like that, and even then not getting brand new data.
My solution was to make the script that gets the data start running at the same time as the launch function. Here was the final script for the first intent (the rest of it remained the same):
#ask.intent("start_skill")
def start_skill():
welcome_message = 'What is the password?'
thread = threading.Thread(target=getData, args=())
thread.daemon = True
thread.start()
return question(welcome_message)
def getData():
#script to get data here
#other intents and rest of script here
By design, the skill requested a numeric passcode to make sure I was the one using it before it was willing to read the data (which was probably pointless, but this skill is at least as much for my own educational reasons as for practical reasons, so, for the extra practice, I wanted this to have as many features as I could possibly justify). So, by the time you would actually be able to ask for the data, the script to get the data will have finished running (I have tested this and it seems to work without fail).

Python MP3Play with Threading

I am trying to create an MP3Player with python (not using any fancy GUIs, just basic command-line). You are able to input commands like "playlist" which prints all songs or "play [num]" which plays the specified song in your playlist. I can do this all in the one thread, but what I want is to create another thread so you can do more commands like "add song" or "delete song" while the actual music is playing (instead of the command line waiting for the music to finish). Here is what I have with one thread:
import mp3play, time
clip = mp3play.load(song)
clip.play()
time.sleep(clip.seconds()) #Waits the duration of the song
clip.stop()
#Command line asks for input after this
This works all fine and dandy, but when I try to implement threading into this, like this:
import mp3play, time
def play(songname):
clip = mp3play.load(song)
clip.play()
time.sleep(clip.seconds()) #Waits the duration of the song
clip.stop()
#Get user input here
#Check if user inputted command
#User inputed "play"
thread.start_new_thread(play,("some_random_song.mp3",))
It glitches out. It all seems fine until you close the application half way through the song and the music still keeps running. To stop the music, I have to open Task Manager and end the task. So I thought about having a "stop" command as well, which wouldn't close the thread, but it would use
clip.stop()
I don't know what happens if you try to stop() a clip that isn't running, so I implemented a prevention system (boolean running that checks if it is or not). But now nothing works, so far here is my code:
def play(filename):
global clip
clip.play()
time.sleep(clip.seconds())
clip.stop()
playing = False
clip = ""
#Get user input
#blah blah blah command stuff
#User inputted "play"
thread.start_new_thread(play,("some_random_song.mp3",))
playing = True
#Goes back to command line
#User inputted 'stop' this time
if playing:
clip.stop()
playing = False
But when I try to run this, it gets to clip.play() in the thread but doesnt start it. Im not sure how I can get around this, and if it's possible to do this without threading. Thanks in advance.
It would be better to play MP3's using a different process, using multiprocessing.Process.
Write a function that takes the path to an MP3 file, and start that as a Process.
For technical reasons, the dominant python implementation (from python.org) restricts threading so that only one thread at a time can be executing python bytecode. This will probably never be glitch free.

Categories