Threading-related issue - Python 3 (Discord bot) - python

I tried to look up this issue, but it seems like I've done something I can no longer look up on my own, so I'll need your guys' help. This will probably be a rather difficult one, but I really hope that someone will help me with this one!
The code: (Apologies for the messy code beforehand)
import json, requests, threading, discord, math, random
from requests import adapters
from requests_testadapter import Resp
from discord.ext.commands import Bot
TOKEN = 'SOMETOKENIDTHATICANTREVEALANDYOUKNOWWHY'
BOT_PREFIX = ('!')
url_api = 'http://api.lambdawars.com/player/matches/list/{}' # input the
SteamID64
client = Bot(command_prefix=BOT_PREFIX)
#client.async_event
class LocalFileAdapter(requests.adapters.HTTPAdapter):
def build_response_from_file(self,request,steamid):
file_path = request.url_api.format(steamid)[7:]
with open(file_path, 'rb') as file:
buff = bytearray(file.read())
resp = Resp(buff)
r = self.build_response(request, resp)
return r
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
return self.build_response_from_file(request)
requests_session = requests.session()
requests_session.mount('file://', LocalFileAdapter())
old_response = ''
monitor_ids = []
#client.command(name='addID')
async def monitoring(steamid):
global old_response
monitor_ids.append(steamid)
while True:
await client.say("Loading results for: " + steamid + "...")
print('Loading results for: ' + steamid)
url_new = url_api
response = requests_session.get(url_new.format(steamid))
if response != old_response:
old_response = response
try:
global idresponse
idresponse = str('-\nPlayer ID {} **' + response.json()['matches'][0]['end_state'] + "** on **" +
response.json()['matches'][0]['start_date'] + "** on map **" + response.json()['matches'][0][
'map'] + "**. \nGame mode: **" + response.json()['matches'][0]['mode'] + "**, type: **" +
response.json()['matches'][0]['type'] + "**. \nThe match lasted **" + str(
math.floor(float(response.json()['matches'][0]['duration']) / 60)) + "** minutes").format(id)
except TypeError:
print("duration type error - bust be a number")
# await client.say(' | '.join(god_damnit_sandern))
await client.say(random.choice["God damnit, Sandern! That match failed to be recorded properly. " + '<:hmm:453711378067226630>',
"God damnit, Sandern! He stole your match data!" + '<:hmm:453711378067226630>',
"God damnit, Sandern! Your match data was not found, it's all his fault!" + '<:hmm:453711378067226630>'])
except KeyError:
print("Wrong SteamID64 number!")
# await client.say('|'.join(god_damnit_sandern))
await client.say("-"
"\nThis (" + steamid + ") isn't SteamID64. <:hardfacepalm:453711378272878592> "
"\nCheck for typos?")
except IndexError:
print("This SteamID64 has no game records.")
await client.say('-'
'\nThis user (' + steamid + ') has never ever played Lambda Wars online yet! <:youserious:453711378687983626>'
'\nWhat a loser!')
await client.say(idresponse)
thread = threading.Thread(target=monitoring)
thread.start()
client.run(TOKEN)
I was working on a Discord bot. The idea was to make a command, namely !addID 76561197995806465 where the ID itself is a SteamID64 for tracking API results on the following page: http://api.lambdawars.com/player/matches/list/76561197995806465
This is basically a JSON result with some of its pieces taken out from the first bit (['matches'][0][{a key, such as "duration" or "start_date"}]).
The plan was to make it automatically get the results, as soon as these results are given, using the following piece in the monitoring method:
if response != old_response:
old_response = response
It does work as a command and makes the Discord bot leave a message when given the SteamID64 and an optional number, such as 1 (0 being default):
LamBOTa Wars: -
Player ID 76561198223952276 lost on Sat, 16 Jun 2018 10:23:49 GMT on map hlw_breakout.
Game mode: destroyhq, type: FFA.
The match lasted 13 minutes
(If you would like to see the code for this one, I'll post it)
So when I run my code, I get the following:
Exception in thread Thread-1:
Traceback (most recent call last):
File "D:\Program Files\Python\Python3\lib\threading.py", line 916, in _bootstrap_inner
self.run()
File "D:\Program Files\Python\Python3\lib\threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
TypeError: 'Command' object is not callable
Help would be really appreciated!
Also before you ask, yes, I am a newb at programming in general. That's my first experience with bots.

Related

How to make a loop for API calls Constantly?

I am wondering how do u make a loop for an api call that will keep calling that API, but when I tried making one it didn't work here is the code:
while True:
api_requesting = requests.get("https://api.battlemetrics.com/servers/3411152?include=player", headers=headers)
time.sleep(5)
jsoned_api = api_requesting.json()
function = jsoned_api["included"]
names = []
for person in function:
names.append(person['attributes']['name'])
And this is for e to call upon the request, and parsed it to give me the names of each player etc
#client.command(name='players')
async def createEmbed(ctx):
await ctx.send(f"{len(names)} players are online currently")
urString = ""
for name in names:
urString = urString + "> " + name + "\n"
urString = "```" + urString + "```"
await ctx.send(urString)
So I am wondering how will I make a loop for my request it's all the way at the beginning where it says while true: but when I run it the bot doesn't respond, and doesn't do anything.
If you want your code to stop when the bot does not respond:
success = True
while success:
api_requesting = requests.get("https://api.battlemetrics.com/servers/3411152?include=player", headers=headers)
# Success is True when the response status code is 200
success = api_requesting.status_code==200
But if you want to keep making requests, you can try:
while True:
api_requesting = requests.get("https://api.battlemetrics.com/servers/3411152?include=player", headers=headers)
if api_requesting.status_code == 200:
# Do something when the bot responds
else:
time.sleep(5)
# Do something else when the bot does not respond

Use Python and bleak library to notify a bluetooth GATT device, but the result is not stable

I try to use Python to control some BLE GATT devices.
I find Bleak (https://bleak.readthedocs.io/en/latest/) this library to communicate with GATT device.
When I use it, most of the time it works really well.
But once I try to communicate with a BLE GATT medical equipment, I found I can't always notify with this equipment.
Here is my code, I just do a little changes from Bleak's github example:(https://github.com/hbldh/bleak/blob/develop/examples/enable_notifications.py)
import asyncio
import logging
from bleak import discover
from bleak import BleakClient
devices_dict = {}
devices_list = []
receive_data = []
#To discover BLE devices nearby
async def scan():
dev = await discover()
for i in range(0,len(dev)):
#Print the devices discovered
print("[" + str(i) + "]" + dev[i].address,dev[i].name,dev[i].metadata["uuids"])
#Put devices information into list
devices_dict[dev[i].address] = []
devices_dict[dev[i].address].append(dev[i].name)
devices_dict[dev[i].address].append(dev[i].metadata["uuids"])
devices_list.append(dev[i].address)
#An easy notify function, just print the recieve data
def notification_handler(sender, data):
print(', '.join('{:02x}'.format(x) for x in data))
async def run(address, debug=False):
log = logging.getLogger(__name__)
if debug:
import sys
log.setLevel(logging.DEBUG)
h = logging.StreamHandler(sys.stdout)
h.setLevel(logging.DEBUG)
log.addHandler(h)
async with BleakClient(address) as client:
x = await client.is_connected()
log.info("Connected: {0}".format(x))
for service in client.services:
log.info("[Service] {0}: {1}".format(service.uuid, service.description))
for char in service.characteristics:
if "read" in char.properties:
try:
value = bytes(await client.read_gatt_char(char.uuid))
except Exception as e:
value = str(e).encode()
else:
value = None
log.info(
"\t[Characteristic] {0}: (Handle: {1}) ({2}) | Name: {3}, Value: {4} ".format(
char.uuid,
char.handle,
",".join(char.properties),
char.description,
value,
)
)
for descriptor in char.descriptors:
value = await client.read_gatt_descriptor(descriptor.handle)
log.info(
"\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(
descriptor.uuid, descriptor.handle, bytes(value)
)
)
#Characteristic uuid
CHARACTERISTIC_UUID = "put your characteristic uuid"
await client.start_notify(CHARACTERISTIC_UUID, notification_handler)
await asyncio.sleep(5.0)
await client.stop_notify(CHARACTERISTIC_UUID)
if __name__ == "__main__":
print("Scanning for peripherals...")
#Build an event loop
loop = asyncio.get_event_loop()
#Run the discover event
loop.run_until_complete(scan())
#let user chose the device
index = input('please select device from 0 to ' + str(len(devices_list)) + ":")
index = int(index)
address = devices_list[index]
print("Address is " + address)
#Run notify event
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(run(address, True))
In most situation, this code can work well as this image.
But sometimes (about 25% ratio), this problem just happened:
[0]08:6B:D7:12:F1:33 Nonin3150_502892837['uuid']
[1]1D:BD:4A:69:8B:AB Unknown []
[2]73:15:CD:47:AF:08 Unknown []
[3]40:4E:36:5B:8D:1B HTC BS 1BBDB9 ['uuid']
[4]6B:FB:E5:DD:7F:4E Unknown []
[5]69:A7:87:23:5C:7C Unknown []
please select device from 0 to 6:0
Address is 08:6B:D7:12:F1:33
Traceback (most recent call last):
File "D:/Bletest/nonin_test.py", line 91, in <module>
loop.run_until_complete(run(address, True))
File "C:\Users\rizal\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 579, in run_until_complete
return future.result()
File "D:/Bletest/nonin_test.py", line 36, in run
async with BleakClient(address) as client:
File "C:\Users\rizal\AppData\Local\Programs\Python\Python37\lib\site-packages\bleak\backends\client.py", line 60, in __aenter__
await self.connect()
File "C:\Users\rizal\AppData\Local\Programs\Python\Python37\lib\site-packages\bleak\backends\dotnet\client.py", line 154, in connect
"Device with address {0} was not found.".format(self.address)
bleak.exc.BleakError: Device with address 08:6B:D7:12:F1:33 was not found.
Process finished with exit code 1
I have no idea why the program can discover this equipment, but can't notify with it.
Is this my code's problem, or it may caused by this equipment's program flow?
I do have the same problem. However, I think it has something to do with the implementation of BLE in Windows. When you scan for devices in the Windows interface you are sometimes able to see how devices appear and dissapear. The devices Advertising Interval could be to long.
However, it is fixable by wrapping it in a try catch section.
Something like this could work for you.
async def connect_to_device(self):
while True:
if self.connection_enabled:
try:
await self.client.connect()
self.connected = await self.client.is_connected()
if self.connected:
print("Connected to Device")
self.client.set_disconnected_callback(self.on_disconnect)
await self.client.start_notify(
self.notify_characteristic, self.notify_callback,
)
while True:
if not self.connected:
break
await asyncio.sleep(1.0)
else:
print(f"Failed to connect to Device")
except Exception as e:
print(e)
else:
await asyncio.sleep(1.0)
You just need to add this task to the loop
asyncio.ensure_future(self.connect_to_device(), loop)
define the client
self.client = BleakClient(self.connected_device.address, loop=loop)
and enable the connection
self.connection_enabled = true

Getting 'KeyError' when running module

So, I'm a bit new with the Python code so I'm a bit lost when trying to decypher the issue here.
I keep getting this error when trying to run this module:
Traceback (most recent call last):
File "C:\Users\test\OneDrive\Documents\mass.py", line 33, in <module>
delete_all(auth_token, channel_id, username1, username2, get_all_messages(auth_token, channel_id))
File "C:\Users\test\OneDrive\Documents\mass.py", line 29, in delete_all
if (message["author"]["username"] == user1):
KeyError: 'author'
Here's all of the code right here:
import json, requests, sys
print ("Delete all messages from specific channel")
username1 = "test"
username2 = "test#0101"
auth_token = "ZMNHFHFKJkjfja.FJDJfhsd.EJjfda"
channel_id = "35345345345451"
delete_from_all_users = "False"
def get_all_messages(auth, id, last="", prev=[]):
if not last:
messages = json.loads(requests.get("http://canary.discordapp.com/api/v6/channels/" + id + "/messages", headers={"authorization": auth}, params={"limit": 100}).content)
else:
messages = json.loads(requests.get("http://canary.discordapp.com/api/v6/channels/" + id + "/messages", headers={"authorization": auth}, params={"before" : last, "limit" : 100}).content)
prev.append(messages)
if len(messages) < 100:
print ("Got to end of channel at " + str(len(prev)) + " messages")
return prev
else:
oldest = sorted(messages, key=lambda x: x["timestamp"], reverse=True)[-1]
return get_all_messages(auth, id, last=oldest["id"], prev=prev)
def delete_all(auth, id, user1, user2, messages):
print ("Trying to delete all messages in " + id + " from username " + user1)
for message in messages:
# print(message["author"]["username"])
if (message["author"]["username"] == user1):
requests.delete("http://canary.discordapp.com/api/v6/channels/" + id + "/messages/" + message["id"],headers={"authorization": auth})
print ("All messages were deleted")
delete_all(auth_token, channel_id, username1, username2, get_all_messages(auth_token, channel_id))
Kelsey, in your code in the 4th last line in the if condition,
if (message["author"]["username"] == user1):
the compiler does not find any "author" in the json messages that you are iterating upon. Now since we do not know the structure of your json we cannot help you out more there, but this is for sure that the message json does not contain any key such namely author.

Sending json data over irc (python3)

In this project I am trying to send json data to a specific irc channel. The irc bot is supposed to do two things at a time, check for a certain message, and send the json data (Sorry for repeating json data so often :).)
I created a class, for the function which, searches on a website for the data it is supposed to send over irc (search.py):
import re
import json
import requests
class search:
def run():
data = requests.get("http://boards.4chan.org/g/catalog").text
match = re.match(".*var catalog = (?P<catalog>\{.*\});.*", data)
if not match:
print("Couldn't scrape catalog")
exit(1)
catalog = json.loads(match.group('catalog'))
running = True
while running:
try:
filtertext = ("tox")
for number, thread in catalog['threads'].items():
sub, teaser = thread['sub'], thread['teaser']
if filtertext in sub.lower() or filtertext in teaser.lower():
#liste.append(teaser)
#return(teaser)
#print(liste[0])
return(teaser)
running = False
The file which is supposed to send the data to the data to the irc channel (irctest.py):
import socket,threading,time
from search import search
# Some basic variables used to configure the bot
server = b"irc.freenode.net" # Server
channel = b"#volafile" # Channel
botnick = b"Mybot" # Your bots nick
troo = True
def ping(): # This is our first function! It will respond to server Pings.
ircsock.send(b"PONG :pingis\n")
def sendmsg(chan , msg): # This is the send message function, it simply sends messages to the channel.
ircsock.send(b"PRIVMSG "+ chan +b" :"+ msg +b"\n")
def joinchan(chan): # This function is used to join channels.
ircsock.send(b"JOIN "+ chan + b"\n")
def worker():
print(threading.currentThread().getName(), 'Starting')
while True:
#liste3= search.run()
#teaser1 = str(search.teaser)
ircsock.send(b"PRIVMSG "+ channel + b" :"+ search.run() + b"\n")
print(threading.currentThread().getName(), 'Exiting')
ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ircsock.connect((server, 6667))
ircsock.send(b"USER "+ botnick + b" "+ botnick + b" "+ botnick + b" :This bot is a result of a tutoral covered on http://shellium.org/wiki.\n")
ircsock.send(b"NICK "+ botnick + b"\n")
joinchan(channel) # Join the channel using the functions we previo
time.sleep(10)
w = threading.Thread(name='worker', target=worker)
i=0
w.start()
while 1: # Be careful with these! it might send you to an infinite loop
ircmsg = ircsock.recv(2048) # receive data from the server
ircmsg = ircmsg.strip(b'\n\r') # removing any unnecessary linebreaks.
print(ircmsg) # Here we print what's coming from the server
if ircmsg.find(b":Hello "+ botnick) != -1: # If we can find "Hello Mybot" it will call the function hello()
hello()
if ircmsg.find(b"PING :") != -1: # if the server pings us then we've got to respond!
ping()
but I get this error message:
Exception in thread worker:
Traceback (most recent call last):
File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
File "/usr/lib/python3.4/threading.py", line 868, in run
self._target(*self._args, **self._kwargs)
File "irctest.py", line 24, in worker
ircsock.send(b"PRIVMSG "+ channel + b" :"+ bytes(search.run()) + b"\n"\
TypeError: string argument without an encoding
When you call ircsock.send() in your function worker(), your last string isn't a bytestring ("\n"). You should also cast the search.run() return value to a bytestring with bytes(search.run(), "utf-8"). Change the specific line to:
ircsock.send(b"PRIVMSG "+ channel + b" :"+ bytes(search.run(), "utf-8") + b"\n")

python noob having troubles sending google voice text

I'm writing a simple little script to send me a text message when the Ultra Music Festival early bird tickets go on sale so I can snatch them up. When I came to writing this I figured python would be a quick way to achieve my goal. What I do is collect the links and then count them and determine if there is a change and send a google voice text message to a couple numbers. Here is my code ran against stackoverflow.
from googlevoice import Voice
from googlevoice.util import input
from bs4 import BeautifulSoup, SoupStrainer
from time import sleep
import urllib2
from array import *
#define login details
email = 'example#gmail.com'
password = 'password'
url = 'http://stackoverflow.com/questions'
def send_message(var_text):
voice = Voice()
voice.login(email, password)
phoneNumber = array('L',[9998675309, 9998675309])
for i in phoneNumber:
voice.send_sms(i, var_text)
#init
soup = BeautifulSoup(urllib2.urlopen(url).read(), parse_only=SoupStrainer('a'))
link_count = len(soup)
#start the loop
var = 1
while var == 1 : # This constructs an infinite loop
soup = BeautifulSoup(urllib2.urlopen(url).read(), parse_only=SoupStrainer('a'))
if link_count != len(soup):
string = str('Link Count Changed\n\nSite:\n' + url + '\nPrev:\n' + str(link_count) + '\nNew:\n' + str(len(soup)))
send_message(string)
print (string)
link_count = len(soup)
sleep(10)
pass
else:
print('Number of links ('+ str(link_count) + ') has not changed, going to sleep now.')
sleep(10)
pass
print "Good bye!"
Here is the error I keep getting (only seems to happen when sending to more then one number)
doesn't work array('L',[9998675309, 9998675309])
works array('L',[9998675309])
ERROR:
bash-3.2# python gvsendalert.py
Number of links (195) has not changed, going to sleep now.
Traceback (most recent call last):
File "gvsendalert.py", line 32, in <module>
send_message(string)
File "gvsendalert.py", line 19, in send_message
voice.send_sms(i, var_text)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/googlevoice/voice.py", line 151, in send_sms
self.__validate_special_page('sms', {'phoneNumber': phoneNumber, 'text': text})
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/googlevoice/voice.py", line 225, in __validate_special_page
load_and_validate(self.__do_special_page(page, data))
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/googlevoice/util.py", line 65, in load_and_validate
validate_response(loads(response.read()))
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/googlevoice/util.py", line 59, in validate_response
raise ValidationError('There was a problem with GV: %s' % response)
googlevoice.util.ValidationError: There was a problem with GV: {u'data': {u'code': 58}, u'ok': False}
Ok I've taken into consideration what some of you have posted and come out with this. For the number array sending my google voice number twice it will send 2 messages. If I put my friends number as the second it breaks it. Could this be because my friends number is not a google voice number? I have been able to send messages to this number using Google Voice and some other 3rd party iPhone applications so I would think the python module would work the same way.
Here is my 2nd Revision Code:
def send_message(var_text):
voice = Voice()
voice.login(email, password)
phoneNumber = ['myrealgooglenumber', 'myfriendsactualphonenumber']
for i in phoneNumber:
print( str('sending to: ') + str(i))
voice.send_sms(str(i), str(var_text))
sleep(5)
#init
soup = BeautifulSoup(urllib2.urlopen(url).read(), parse_only=SoupStrainer('a'))
link_count = len(soup)
#start the loop
var = 1
while var == 1 : # This constructs an infinite loop
soup = BeautifulSoup(urllib2.urlopen(url).read(), parse_only=SoupStrainer('a'))
if link_count != len(soup):
string = ('Link Count Changed\n\nSite:\n{0}\nPrev:\n{1}\nNew:\n{2}').format(url, link_count, len(soup))
send_message(string)
link_count = len(soup)
print (string)
sleep(10)
pass
else:
string = ('Number of links ({0}) has not changed, going to sleep now.').format(str(link_count))
print(string)
sleep(10)
pass
print "Good bye!"
Have tested with 2 google voice numbers and it works. Still doesn't work with non google voice numbers.
It looks like you're using ints for the phone numbers.
Phone numbers are not true numbers.
Try strings instead:
phoneNumber = ['9998675309', '9998675309']
Also, on a style note, have a look at string formatting:
string = 'Link Count Changed\n\nSite:\n{0}\nPrev:\n{1}\nNew:\n{2}').format(url, link_count, len(soup))
Google may have a timer to prevent you sending too many SMS messages back to back.
Perhaps you could try changing your loop to something like:
for i in phoneNumber:
voice.send_sms(i, var_text)
sleep(5)
One other thought, does it work better if you use 2 different phone numbers?

Categories