I'm getting strings of game-chat from a server and I need to check if a user is mentioned in that string and if he is, I need to find him on the server and mention him because I can't just send the string as it is as it's not mentioning him.
Here's a simple example:
socket_str = "Hey this is a ping test for #TheBeast"
I need to check for tags on that string (#) , then get the name separated so TheBeast , then I need to go over the members in the servers and find a member object with that name and build a final fstring that contains the string before the mention, and with the mention.
so it will look the same but the bot will actually mention this user.
This was the simplest example, but there are so many edge cases that I can't deal with, for example, what if the the person has spaces in his name, how do you know when the name ends? Here's is the most complicated example I could make:
socket_str = "Hey I'm looking for #The New Beast is he online?, or #Newly Born Beast or #someone that doesnt exists is on?"
I'm looking for a different approach for this, I could share what I wrote so far which is a lot but honestly it's so complex code even I don't understand from it much anymore
This is actually very non-trivial. You've already said it yourself
"if the the person has spaces in his name, how do you know when the name ends?"
The only option I can think of to reliably check if a username (containing spaces) exists is to iteratively check each combination of spaced words as long as a certain semantic criteria is met.
In Discord, the only restrictions usernames have is that it can be at max 32 characters long. AFAIK you can have every symbol, emoji whatsoever in your name...
To illustrate, the statements would look something like this
string = "Hello #This is a username! Whats up?"
# is "This" a username?
# yes -> great! | no -> is "This is" a username?
# yes -> great! | no -> is "This is a" a username?
# yes -> great! | no -> is "This is a username!" a username?
# ...
However, this is also another edge case. This is a username is a valid user, but by spliting with spaces the program would look for This is a username!, which isn't valid. So as far as I can tell, the best option to say for sure, if a username is valid is to actually check for each character until the max length of Discord usernames.
This could be implemented like so
string = "Hello #This is a username! Whats up?"
potentialUsernames = string.split("#") # Split string into potential usernames
del potentialUsernames [0] # Delete first element, as it is definitely not a username
for potentialUsername in potentialUsernames: # for every potential username, do
for run, letter in enumerate(potentialUsername): # for every letter in every potential username, do
checkUsername = potentialUsername[:(run+1)]
if run > 32:
break # break out of this loop as there cant be a username here anymore
potentialMember = guild.get_member_named(checkUsername) # check if potential username exists
if potentialMember != None: # BOOM, we found a member!
string = string.replace("#" + checkUsername, potentialMember.mention) # replace the username with real mention in string
break # break because the user was already found
The output of print(string) would be
"Hello <#!1234567891011121314>! Whats up?"
yes.. this is how mentions would look in text-form, if you didn't know. The long number would be the user-id, however Member.mention already constructs this for you!
In this code, guild will have to be the guild object, of which you want to get the members from.
Now, what this code does is checking every potential username split by #, and check for every possible length until the next #, or Discords restriction of 32 characters.
I.e.
# is "T" a username?
# yes -> great | no -> is "Th" a username?
# yes -> great | no -> is "Thi" a username?
# ...
As a side note, this method should work with any amount of mentions!
Related
Building a webscraper for a game I love and right now came into a little issue with python, what I want to do could best be show as:
userdata = { ' ', [ ]}
I'm new to writing python so my question is would this work given the following scenario:
User's account name (which players in the game use to message each other) wants to attach multiple character names to that single account name so other players know that all these characters belong to that one account.
The end result should be something like this:
"Please enter your account name followed by which characters you wish associated with this account:"
user input: Leafzer Leaf1 Leaf2 Leaf3
current limitations in the game work to python's advantage as account and character names can not have any white space. I was considering using split such as:
x = [str(x) for x in input("Enter user data: ").split()]
but as it stands neither of these seem to work quite right.
To reiterate and maybe clear some of the confusion: Writing a website scraper that allows players in the game to enter their account name and a list of mules (characters in the game that just hold items). The list of mules and the account name must be separate as my scraper uses the list of mules to go to a certain website using that mule name and downloads the data into a searchable csv file. If another player searches for an item that is within that csv file, it brings up the account name associated with the mule rather than the mule name and displays the account name to player searching for the item.
I'm stuck trying to figure out how to obtain the user data in this manner. Any help would be appreciated.
Are you after something like this:
users = {}
user, *chars = input("Please input your name and characters: ").split(" ")
users[user] = chars
?
Or slightly less confusingly (but not nearly as neatly):
users = {}
words = input("Please input your name and characters: ").split(" ")
user = words[0]
chars = words[1:]
users[user] = chars
But * unpacking is great, and everyone should use it!
P.S. for your second use case, just call input() twice!:
username = input("Please input your name: ")
chars = input("Please input your characters: ").split(" ")
I have a discord bot that can take in commands and store info in text files.
My goal is to have different items that people can add numbers to and then check their totals for each item.
For example, if I did !add 200 oranges, the bot would add the number 200 with the user id to the oranges.txt file. I would have different files for each item. If I then did !add 300 oranges, the number next to the user id with be 500. I would then want to let a user check their totals.
right now I have this:
#bot.command(pass_context = True)
async def loot(ctx, num, kind):
author = str(ctx.message.author)
message = list()
message.append(num)
message.append(kind)
#below line for testing
await bot.say(message)
The code takes in the number and name of the item, then creates a list [num, item]. My idea is then to use message[1] to find which text file to input in, and then somehow add message[0] with the author variable which is the user id to a list, but at that point im lost. I dont know how to add an author id + number, add to the number, and then retrieve it.
If you need any clarification please ask!
This sounds a job for a DBMS. Check out https://www.sqlite.org/, or do some research and pick another one. It allows for a structured way to store data, and eliminates these kinds of problems.
Sorry if this does answer your actual question, but there is an easier solution to the problem you are having. Storing data like this in text files is widely regarded as bad practice (not even taking into account what kind of security problems this can yield.)
datadict = {}#open the file
with open('phoneproblemquery.txt') as file:
for line in file:
problem, answer = line.split('-')
problems = problem.strip().split(' ')
for item in problems:
datadict[item] = answer
user_problem = input('What is the problem?:')
print(datadict[user_problem])
The text-file contains something like this:
screen - replace screen.
if I were to run this program and enter in 'screen' the program will respond 'replace screen'. but, if I were to enter something like 'the screen'(not just 'screen' alone) the program will give a 'keyError' and won't work.
what would I need to do to if the user enters 'the screen' (instead of just 'screen') for the program to provide an output 'replace screen'. would I need to put the users answer into arrays? if so how?
Thanks!
update: 'the screen' was just an example. The user can enter in any
form of way i.e 'screen is...' the keyword is screen. I would want
the program to identify the key word from the users input and get
the response 'replace screen'. ... ;( desperate for an answer...
You want the program to pick keywords out of the user input and look for them in datadict. The difficulty with that is picking out keywords. A simple approach is to have regular expressions rather than simple keywords as the keys of your lookup dictionary. You will of course have to do the matching yourself: the dictionary lookup process won't do it for you.
import re
datadict = {}
#open the file
with open(r'phoneproblemquery.txt') as file:
for line in file:
problem, answer = line.split('-')
problems = problem.strip().split(' ')
for item in problems:
datadict[re.compile(fr'{item}', re.IGNORECASE)] = answer
user_problem = input('What is the problem?:')
for regex, diagnosis in datadict.items():
if regex.search(user_problem):
print (diagnosis)
This will work, but in a more sophisticated implementation it might be better to put the regular expressions in the input data, rather than constructing them at runtime like I have done here. You are already allowing for something of the sort by having a space-delimited list if keywords in your input data. If you had regular expressions in the input instead, the same would apply, only instead of, say
keyboard kybd - replace keyboard
you would have
keyboard|kybd - replace keyboard
and in that event maybe a hyphen would not be the best delimiter. A carriage return might be better, since that can never appear in the input.
I have a question which asks me to get a user's email address and then return the URL it is associated with. So, for example: 'abc123#address.com' --> 'http:://www.address.com'
I did get this:
def main():
email_address = input('Enter your email address (eg. abc123#address.com): ').strip()
strip_username = email_address.split('#', 1)[-1]
the_url(strip_username)
def the_url(url_ending):
print('Your associated URL is: http://www.' + str(url_ending))
main()
which does what I want, but this code: split('#'...) is something I haven't learned yet. I just found it online. I need to use indexing and splicing for this program, but how can I use splicing if I don't know the length of the user's email? I need to get rid of everything before and including the '#' symbol so that it can leave me with just 'address.com' but I don't know what address it will be. It could be hotmail, gmail, etc. Thanks, and I'm really new to Python so I'm trying to only use what I've learned in class so far.
The split method just splits up the string based on the character to you give it, so:
"Hello#cat".split("#")
Will give you
["Hello", "cat"]
Then you can just take the 1st index of that array to give you whatever's after the first # symbol.
If you don't want to use str.split then by indexing and slicing,
you can do something like this.
>>> str = 'abc123#address.com'
>>> 'http://www.' + str[str.index('#')+1:]
'http://www.address.com'
I'm writing a registration form that only needs to accept the local component of a desired email address. The domain component is fixed to the site. I am attempting to validate it by selectively copying from validators.validate_email which Django provides for EmailField:
email_re = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
# quoted-string, see also http://tools.ietf.org/html/rfc2822#section-3.2.5
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"'
r')#((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$)' # domain
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE) # literal form, ipv4 address (SMTP 4.1.3)
validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
Following is my code. My main issue is that I'm unable to adapt the regex. At this point I'm only testing it in a regex tester at http://www.pythonregex.com/ however it's failing:
^([-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*)$
This seems to be passing undesirable characters such as ?
The entire code for my Field, which is not necessarily relevant at this stage but I wouldn't mind some comment on it would be:
class LocalEmailField(CharField):
email_local_re = re.compile(r"^([-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*)$", re.IGNORECASE)
validate_email_local = RegexValidator(email_re, (u'Enter a valid e-mail username.'), 'invalid')
default_validators = [validate_email_local]
EDIT: To clarify, the user is only entering the text BEFORE the #, hence why I have no need to validate the #domain.com in the validator.
EDIT 2: So the form field and label will look like this:
Desired Email Address: [---type-able area---] #domain.com
You say "undesirable characters such as ?", but I think you're mistaken about what characters are desirable. The original regex allows question marks.
Note that you can also define your own validator that doesn't use a massive regex, and have some chance of decoding the logic later.
Some people, when confronted with a problem, think, “I know, I’ll use
regular expressions.” Now they have two problems. - Jamie
Zawinski
Checking via regex is an exercise in wasting your time. The best way is to attempt delivery; this way not only can you verify the email address, but also if the mailbox is actually active and can receive emails.
Otherwise you'll end up in an every-expanding regular expression that can't possibly hope to match all the rules.
"Haha boo hoo woo woo!"#foo.com is a valid address, so is qwerterukeriouo#gmail.com
Instead, offer the almost-standard "Please click on the link in the email we sent to blahblah#goo.com to verify your address." approach.
If you want to create email addresses, then you can write your own rules on what can be a part of the email component; and they can be a subset of the official allowed chars in the RFC.
For example, a conservative rule (that doesn't use regular expressions):
allowed_chars = [string.digits+string.letters+'-']
if len([x in user_input if x not in allowed_chars]):
print 'Sorry, invalid characters'
else:
if user_input[0] in string.digits+'-':
print 'Cannot start with a number or `-`'
else:
if check_if_already_exists(user_input):
print 'Sorry, already taken'
else:
print 'Congratulations!'
I'm still new to Django and Python, but why reinvent the wheel and maintain your own regex? If, apart from wanting users to enter only the local portion of their email address, you're happy with Django's built-in EmailField, you can subclass it quite easily and tweak the validation logic a bit:
DOMAIN_NAME = u'foo.com'
class LocalEmailField(models.EmailField):
def clean(local_part):
whole_address = '%s#%s' % (local_part, DOMAIN_NAME)
clean_address = super(LocalEmailField, self).clean(whole_address)
# Can do more checking here if necessary
clean_local, at_sign, clean_domain = clean_address.rpartition('#')
return clean_local
Have you looked at the documentation for Form and Field Validation and the .clean() method?
If you want to do it 100% correctly with regex, you need to use an engine with some form of extended regex which allow matching nested parentheses.
Python's default engine does not allow this, so you're better off compromising with a very simple (permissive) regex.