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?
Related
Background: im trying to send RSVPs(wedding to numbers from guest list in excel
Method:
im using pywhatkit to send a message with a link to RSVP page with the following code:
import pywhatkit
import openpyxl
# extracting numbers from excel
wb = openpyxl.load_workbook('wedding.xlsx')
ws = wb["Sheet1"]
num = []
for row in ws.iter_rows(min_row=1, max_col=1, values_only=True):
for value in row:
num.append('+' + str(value))
# message content
text1 = "please confirm RSVP in https://*****/?phone="
text2 = " \n _please do not reply to this number_"
# sending the links
for x in num:
pywhatkit.sendwhatmsg_instantly( x , text1 + x + text2, 10, True, 10)
for just the example lets use:
for x in range(10):
pywhatkit.sendwhatmsg_instantly( '+****' , 'test' + str(x), 10, True, 10)
i tried to change the delays to [12,3],[5,5],[5,10] and a few more
Result: sending the message fails sometimes. so if we look at the example - i get 8/10 of the messages. it seems like in those times it fails it writes the message in the text box but doesnt press send.
keep in mind that i need to send this to 400 people so i cant set long delays
is there a fix to this?
is there a way to check if the message was sent so it can retry?
any other librarys that are more stable?
Today I wrote a twitter bot that replies anybody who mentions it with a random image from a folder.
The problem here is that I'm a newbie in python and I don't know how to make it funcitonal at all. When I started running it, the bot started replying all the mentions from other users (I'm using an old account a friend gave to me), and that's not precisely what I want, even if it's working, but not as I desire.
The bot is replying all the mentions from the very beggining and it won't stop until all these are replied (the bot is now turned off, I don't want to annoy anybody)
How can I make it to only reply to latest mentions instead of the first ones?
here's the code:
import tweepy
import logging
from config import create_api
import time
import os
import random
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()
api = create_api()
imagePath = random.choice(os.listdir("images/"))
while True:
for tweet in tweepy.Cursor(api.mentions_timeline).items():
try:
imagePath = random.choice(os.listdir("images/"))
tweetId = tweet.user.id
username = tweet.user.screen_name
api.update_with_media('images/' + imagePath, "#" + username + " ", in_reply_to_status_id=tweet.id)
print('Replying to ' + username + 'with ' + imagePath)
except tweepy.TweepError as e:
print(e.reason)
except StopIteration:
break
time.sleep(12)
Thanks in advance.
I don't have the ability to test this code currently but this should work.
Instead of iterating over every tweet, it turns the iterator that tweepy.Cursor returns into a list and then just gets the last item in that list.
api = create_api()
imagePath = random.choice(os.listdir("images/"))
while True:
tweet_iterator = tweepy.Cursor(api.mentions_timeline).items()
latest_tweet = list(tweet_iterator)[-1]
try:
imagePath = random.choice(os.listdir("images/"))
tweetId = latest_tweet.user.id
username = latest_tweet.user.screen_name
api.update_with_media('images/' + imagePath, "#" + username + " ", in_reply_to_status_id=latest_tweet.id)
print('Replying to ' + username + 'with ' + imagePath)
except tweepy.TweepError as e:
print(e.reason)
except StopIteration:
break
time.sleep(12)
You will also want to keep track of what user you last replied to, so you don't just keep spamming the same person over and over.
This isn't the most efficient way of doing it but should be easy enough to understand:
latest_user_id = None
while True:
# Rest of the code
try:
if latest_user_id == latest_tweet.user.id:
# don't do anything
else:
latest_user_id = latest_tweet.user.id
# the rest of your code
my first post here. If you check out /r/EDH right now you'll see my bot on the front page. The idea is that when you type ?edhbot [name of a card]? it gives you links to relevant pages on edhrec.com.
For some reason however it seems to ignore some people and my bot isn't reporting any rate limit issues. They're not getting the wakeword wrong, so I really can't work out what they're doing wrong? Here's my code for reference:
#imported modules
import praw
import string
#functions
def main():
#API login
#I have no idea what I can't show so I redacted most lol
reddit = praw.Reddit(client_id="REDACTED",
client_secret = "REDACTED",
user_agent = "a bot for my epq, by aggressivechairs",
username = "EPQ_MTG_BOT",
password = "REDACTED")
subreddit = reddit.subreddit("all")
wakeword = "?edhbot "
for comment in subreddit.stream.comments():
if wakewordCheck(comment, wakeword): reply = generateComment(comment)
#Checks comment to see if it contains the wake word for the bot
def wakewordCheck(comment, wakeword):
if wakeword in comment.body: return(True)
else: return(False)
#Generates the link extension
def linkGenerator(inString):
startPoint = inString.index("?edhbot ")
inString = inString.lower()
outString = ""
for i in range(startPoint+8, len(inString)):
if inString[i] == "?":
break
elif inString[i] in string.punctuation:
continue
elif inString[i] in string.whitespace:
outString += "-"
continue
outString += inString[i]
return(outString)
#Generates the text that will be used in the bot's reply
def generateComment(comment):
try:
normalisedLink = linkGenerator(comment.body)
commentReply = "[EDHRec page for the commander](https://edhrec.com/commanders/"+normalisedLink+") \n\n [EDHREC page for that as a card](https://edhrec.com/cards/"+normalisedLink+")"
comment.reply(commentReply)
print("I did it with " + comment.body)
except Exception as e: print(e)
if __name__ == "__main__":
main()
So yeah, when you use type ?edhbot it either reads until the end of the comment or until it encounters a new question mark. If you check out this thread though you can see that it just elects to ignore some people. https://www.reddit.com/r/EDH/comments/9ec677/im_a_new_bot_made_just_for_this_sub/?sort=new
What do you suggest? I can't work out what's going wrong :/ Oh and if you spot any bad coding practice feel free to tell me! I want to improve haha
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.
I found this python script at metafilter and modified the addresses and pass as needed, but I get a script error at the very last line. In the error the little carat symbol is underneath the quote after print "\n
Not being a coder at all, I have no idea where to turn next. Any help would be greatly appreciated.
What I actually want to do is pull out the "From" addresses and not to and CC, but I figured I would get this working properly first.
Script was run in Windows using Python 3.2 and redirected output to a text file.
import email
import getpass
import imaplib
HOST = "mail.-----.com"
USER = "sales#-----.com"
FOLDER = "Folder"
connection = imaplib.IMAP4_SSL(HOST)
res, data = connection.login(USER, getpass.getpass())
assert res == "OK"
res, count = connection.select(FOLDER)
assert res == "OK"
res, (msg_nums,) = connection.search(None, "ALL")
assert res == "OK"
for msg_num in msg_nums.split():
res, message_text = connection.fetch(msg_num, "(RFC822)")
assert res == "OK"
message = email.message_from_string(message_text[0][1])
tos = message.get_all("From") or []
ccs = message.get_all("Cc") or []
all_recipients = email.Utils.getaddresses(tos + ccs)
print "\n".join(addr.lower() for realname, addr in all_recipients)
print is a function in Python 3, so it needs to be:
print("\n".join(addr.lower() for realname, addr in all_recipients))
The script was probably originally written for use with Python 2.x.