Getting 'KeyError' when running module - python

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.

Related

Why am I getting this error? (KeyError: 'username')

I'm asking the user for an email, and then sending it to an email verification api, which I then get certain bits of info from. I'm getting a KeyError: 'username' and I have no idea why I'm getting that error. It's also annoying to test since they ratelimit after ~5 attempts
import json, requests, sys
emailInput = ""
def printHelp():
print("Proper usage is: python test.py [email]")
if len(sys.argv) < 2:
printHelp()
sys.exit()
elif len(sys.argv) == 2 and sys.argv[1] == "--help":
printHelp()
sys.exit()
elif len(sys.argv) == 2 and sys.argv[1] != "--help":
emailInput = str(sys.argv[1])
url = 'https://api.trumail.io/v2/lookups/json?email=' + str(emailInput)
res = requests.get(url)
res.raise_for_status()
resultText = res.text
emailInfo = json.loads(resultText)
print("\nEmail Analyzer 1.0\n\nInformation for email: " + sys.argv[1])
print("=====================================================")
print("Username: " + str(emailInfo["username"]))
print("Domain: " + str(emailInfo["domain"]))
print("Valid Format: " + str(emailInfo["validFormat"]))
print("Deliverable: " + str(emailInfo["deliverable"]))
print("Full Inbox: " + str(emailInfo["fullInbox"]))
print("Host Exists: " + str(emailInfo["hostExists"]))
print("Catch All: " + str(emailInfo["catchAll"]))
print("Disposable: " + str(emailInfo["disposable"]))
print("Free: " + str(emailInfo["free"]))
The reason is because a user enters an email that might seem valid - i.e. it's a valid email address with an # symbol etc. - but the email likely does not exist or is not in use.
For example, I ran your script with the following dummy input:
emailInput = 'acdefg#gmail.com'
After I added a print(emailInfo) statement for debugging purposes, this is what I found to be the output from the server:
{'Message': 'No response received from mail server'}
Therefore, your goal here will be to validate the server output. That is, in the case of a valid email that does not exist, you will receive an HTTP 200 (OK) response from the server with a Message field alone populated in the JSON response object. The task here will be to correctly detect the presence of this key, and then run a separate logic other than the happy path, which was already being handled above.
Your error is coming from the fact that emailInfo does not have a key username. Perhaps use emailInfo.get("username", default_value), where default_value is any value you would like if there is no username.
The line with the error is print("Username: " + str(emailInfo["username"]))

html2text wants a bytes like object but is rejecting something of class bytes

I am trying to parse some emails using python and I am hitting a wall with html2text. The project is to take a bunch of emails that are responses to a form and get that into a workable csv format. You can invasion the body of the mail being akin to
name: <name>
email: <email address>
grade level: <grade level>
interests: <interests>
(etc)
I need get the body of the email and parse it from raw HTML to text so that I can work through it for questions and their corresponding answers. This is my code so far:
#!/usr/bin/env python3
""" Access email and get contents to csv """
import datetime, os, glob
import email, html2text, imaplib
EMAIL_UN = '<email address>'
EMAIL_PW = '<app token>'
MENTOR = 'Form Submission - Mentor Application'
MENTEE = 'Form Submission - Mentee Application'
cwd = os.getcwd()
sentSince = "SENTSINCE 01-Oct-2021"
def download_emails(SUBJECT):
un = EMAIL_UN
pw = EMAIL_PW
url = 'imap.gmail.com'
folder = "\"" + "Mentoring Program" + "\""
complexSearch = sentSince + " Subject " + "\"" + SUBJECT + "\""
detach_dir = cwd # directory where to save output (default: current)
# connecting to the gmail imap server
m = imaplib.IMAP4_SSL(url,993)
m.login(un,pw)
m.select(folder)
# # This allows us to cycle through the folders and labels in Gmail to find what we need
# for items in m.list('/'):
# for item in items:
# print(item)
resp, items = m.search(None, complexSearch)
# you could filter using the IMAP rules here (check http://www.example-code.com/csharp/imap-search-critera.asp)
items = items[0].split() # getting the mails id
print("Items: \n{}".format(items))
results = [] # This will be a list of dicts, to breakdown each email
for emailid in items:
resp, data = m.fetch(emailid, "(RFC822)") # fetching the mail, "`(RFC822)`" means "get the whole stuff", but you can ask for headers only, etc
if resp != 'OK':
raise Exception("Error reading email: {}".format(data))
message = email.message_from_string(str(data[0][1])) # parsing the mail content to get a mail object
# Next parse the mail object to get a dictionary with important data, text = body of email
res = {
'From' : email.utils.parseaddr(message['From'])[1],
'From name' : email.utils.parseaddr(message['From'])[0],
'Time' : message['Date'],
'To' : message['To'],
'Text' : '',
'File' : None
}
# message.walk is a generator that allows us to walk through the parts of the email
for part in message.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get_content_maintype() == 'text':
# reading as HTML (not plain text)
_html = part.get_payload(decode = True)
print(type(_html)) # checking the type to make sure it's bytes
res['Text'] = html2text.html2text(_html)
results.append(res)
print(results)
if __name__ == '__main__':
download = input("Do you want to search for Mentor or Mentee forms? (enter: Mentor / Mentee) \n")
if download.upper() == 'MENTOR':
download_emails(MENTOR)
elif download.upper() == 'MENTEE':
download_emails(MENTEE)
else:
print('Usage: ./download_emails.py # prompt enter Mentor or Mentee:')
The issue I am running into is that html2text is complaining about needing a bytes like object, but the class of _email is bytes.... what am I messing up here?? You can see the result of print(type(_html)) right before the error.
The error:
<class 'bytes'>
Traceback (most recent call last):
File "Email_ScriptV0.0.1.py", line 97, in <module>
download_emails(MENTOR)
File "Email_ScriptV0.0.1.py", line 61, in download_emails
res['Text'] = html2text.html2text(_html)
File "/opt/anaconda3/lib/python3.8/site-packages/html2text/__init__.py", line 947, in html2text
return h.handle(html)
File "/opt/anaconda3/lib/python3.8/site-packages/html2text/__init__.py", line 142, in handle
self.feed(data)
File "/opt/anaconda3/lib/python3.8/site-packages/html2text/__init__.py", line 138, in feed
data = data.replace("</' + 'script>", "</ignore>")
TypeError: a bytes-like object is required, not 'str'
Thanks for any and all help!

Where and how to insert 'try and except' in this code to avoid program crash?

I have this code to download emails and save the text body. It saves the text body as a text file using the text in the subject field as the name of the file. The subject SHOULD basically be a student number + school week number, like:
1234567891week8
pathToFiles = '/home/pedro/getEmailtexts/emailTexts17BE/'
server = IMAPClient(HOST, use_uid=True, ssl=True)
server.login(USERNAME, PASSWORD)
select_info = server.select_folder('Inbox')
unseenMessages = server.search(['UNSEEN'])
print('Number of unseen messages is ' + str(len(unseenMessages)))
for uid, message_data in server.fetch(unseenMessages, 'RFC822').items():
email_message = email.message_from_bytes(message_data[b'RFC822'])
print(' message UID is ' + str(uid))
print(email_message.get('Subject'))
messageSubject = email_message.get('Subject')
file = messageSubject + '.txt'
theFile = open(pathToFiles + file, 'w')
rawMessage = server.fetch(unseenMessages, ['BODY[]', 'FLAGS'])
message = pyzmail.PyzMessage.factory(rawMessage[uid][b'BODY[]'])
text = message.text_part.get_payload().decode(message.text_part.charset)
saveText = text.rstrip()
theFile.write(saveText)
theFile.close()
However some students, or their email programs, are putting something weird in the subject field, causing a breakdown. Here is a sample output from my bash terminal:
1725010108week8
message UID is 33
1725010135week8
message UID is 34
1725010126 week8
message UID is 35
������������������1725010118week8
Traceback (most recent call last):
File "./getAnswersFromEmail17BEv2.py", line 45, in <module>
file = messageSubject + '.txt'
TypeError: unsupported operand type(s) for +: 'Header' and 'str'
pedro#pedro-newssd:~/getEmailtexts/python$
I have to go to the email, delete the offending email, and start again.
I think I may be able to insert a try ... except ... in there somehow, but I can't see exactly how. Or maybe there is some other way to deal with a dodgy subject
Do you have any tips for an amateur on how to get round this? How to make the program go to the next email on this kind of error?
My first suggestion would be converting the messageSubject variable to a string.
i.e
file = str(messageSubject) + '.txt'
To use the try except clause, the following snippet will allow the code to move onto the next email if it cannot create the file variable for the email
try:
file = messageSubject + '.txt'
except TypeError:
continue
# Alexis Lucattini: Thank you very much!
This solved crazy subject problems:
messageSubject = str(email_message.get('Subject'))
Then I got " AttributeError: 'NoneType' object has no attribute 'get_payload'"
In the end, I used both suggestions.
At least 1 student must have sent an empty email, which caused problems like this:
UID is 421
1825010336week8
Message subject is 1825010336week8
UID is 424
1825010334Week
Message subject is 1825010334Week
UID is 425
=?gb18030?B?MTgyNTAxMDIzNyDA7s7Ex78=?=
Message subject is =?gb18030?B?MTgyNTAxMDIzNyDA7s7Ex78=?=
UID is 426
=?gb18030?B?ufnT7ubDIDE4MjUwMTAyNDQ=?=
Message subject is =?gb18030?B?ufnT7ubDIDE4MjUwMTAyNDQ=?=
UID is 430
=?gb18030?B?MTgyNTAxMDExMyxBLEIsQSxCLEEsQyxELEcsQSxD?=
=?gb18030?B?LEYsSCxBLEQsQixBLEgsRyxDLEEsQSxCLEEsQSxC?=
=?gb18030?B?LEIsRixELEosRSxBLEcsQyxILEmBMIQyCgoK?=
Message subject is =?gb18030?B?MTgyNTAxMDExMyxBLEIsQSxCLEEsQyxELEcsQSxD?=
=?gb18030?B?LEYsSCxBLEQsQixBLEgsRyxDLEEsQSxCLEEsQSxC?=
=?gb18030?B?LEIsRixELEosRSxBLEcsQyxILEmBMIQyCgoK?=
UID is 431
=?gb18030?B?MTgyNTAxMDEzMSxBLEIsQSxCLEEsQyxELEcsQSxD?=
=?gb18030?B?LEYsSCxBLEQsQixBLEgsRyxDLEEsQSxCLEEsQSxC?=
=?gb18030?B?LEIsRixELEosRSxBLEksQyxILEcKCgo=?=
Message subject is =?gb18030?B?MTgyNTAxMDEzMSxBLEIsQSxCLEEsQyxELEcsQSxD?=
=?gb18030?B?LEYsSCxBLEQsQixBLEgsRyxDLEEsQSxCLEEsQSxC?=
=?gb18030?B?LEIsRixELEosRSxBLEksQyxILEcKCgo=?=
UID is 432
1825010207week8
Message subject is 1825010207week8
UID is 434
������������������1825010136week7
Message subject is ������������������1825010136week7
Traceback (most recent call last):
File "./getAnswersFromEmail18BEv2.py", line 52, in <module>
text = message.text_part.get_payload().decode(message.text_part.charset)
AttributeError: 'NoneType' object has no attribute 'get_payload'
pedro#pedro-newssd:~/getEmailtexts/python$ ^C
pedro#pedro-newssd:~/getEmailtexts/python$
So I put a try except in for that error:
for uid, message_data in server.fetch(unseenMessages, 'RFC822').items():
email_message = email.message_from_bytes(message_data[b'RFC822'])
print('UID is ' + str(uid))
print(email_message.get('Subject'))
messageSubject = str(email_message.get('Subject'))
print('Message subject is ' + messageSubject)
if messageSubject == None:
messageSubject = 'idiot'
file = messageSubject + '.txt'
theFile = open(pathToFiles + file, 'w')
rawMessage = server.fetch(unseenMessages, ['BODY[]', 'FLAGS'])
try:
message = pyzmail.PyzMessage.factory(rawMessage[uid][b'BODY[]'])
text = message.text_part.get_payload().decode(message.text_part.charset)
saveText = text.rstrip()
theFile.write(saveText)
theFile.close()
except AttributeError:
continue
After that, all emails downloaded without a problem.
That is good, because now I can use this routine and the routine which marks the answers and writes the scores to an Excel file all together in 1 program.
Thanks for the advice!

Threading-related issue - Python 3 (Discord bot)

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.

Identify sending user, Python IRC

So I made a Twitch.tv bot for my own channel, after having fun with it a little bit, I wanted to have some command restricted to some users, and some commands that can say the users name, for example:
Username reply example:
Person1: !tea
PythonBot: Would you like some tea, Person1?
Admin restriction example:
Person1: !ban Person2
PythonBot: I'm sorry, Person1, This command is restricted to admins only.
Ok, So here is the code I'm using (I will be modifying it soon to make it my own)
import socket
import threading
bot_owner = '~Not Today~'
nick = '~Not Today~'
channel = '~Not Today~'
server = 'irc.twitch.tv'
password = '~Not Today~'
queue = 13
irc = socket.socket()
irc.connect((server, 6667))
irc.send('PASS ' + password + '\r\n')
irc.send('USER ' + nick + ' 0 * :' + bot_owner + '\r\n')
irc.send('NICK ' + nick + '\r\n')
irc.send('JOIN ' + channel + '\r\n')
def message(msg):
global queue
queue = 5
if queue < 20:
irc.send('PRIVMSG' + channel + ' :' + msg + '\r\n')
else:
print 'Message Deleted'
def queuetimer():
global queue
queue = 0
threading.Timer(30,queuetimer).start()
queuetimer()
while True:
botdata = irc.recv(1204)
botuser = botdata.split(':')[1]
botuser = botuser.split('!')[0]
print botdata
if botdata.find('PING') != -1:
irc.send(botdata.replace('PING', 'PONG'))
if botdata.find('!res') != -1:
irc.send(botdata.replace('!res', '1600x900'))
The twitch IRC raw message is like
:jkm!jkm#jkm.tmi.twitch.tv PRIVMSG #trumpsc :needs Kappa
for above msg, it actually means userjkm at channel trumpsc saying needs Kappa
for your code, the method to get botuser is right, but you don't have the message the user sent, add following code should get the message
botmsg = botdata.split(':')[2]
so you get the message and username, the next step would be handling them.
Here would be some example for your need. For me, I will prepared a adminuserList and commmandList for other command, but I will simplify it here.
def commmanHandler(botuser, botmsg): # botmsg = '!ban user1'
command = botmsg.split(' ')[0] # command = '!ban'
command = command[1:] # command = 'ban'
argu = message.split(' ')[1] # argu = 'user1'
if command not in commmandList:
raise Exception("command not support")
if command == 'ban': # ban command, or using switch
# check if admin
if botuser not in adminList:
raise Exception("I'm sorry, " + botuser + ", This command is restricted to admins only.")
# admin, able to ban
irc.send('PRIVMSG' + channel + ' :' + '.ban ' + argu)
then call this function in your while loop to handle all message you get
try:
commmanHandler(botuser, botmsg)
except Exception, e:
print e
irc.send('PRIVMSG' + channel + ' :' + e)
here would be my solution, and also, don't forget to give the bot moderator privilege.

Categories