kootenpv / yagmail

Send email in Python conveniently for gmail using yagmail
MIT License
2.64k stars 263 forks source link

How do you use local attached images in html? #187

Open 1dancook opened 3 years ago

1dancook commented 3 years ago

Hi, thanks for this yagmail! It's awesome.

I'd like to use attached images in html but I'm not sure how this can be done.

Example html:

<h1>Test Title</h1>
<p><a href="https://www.google.com"><img alt="image" src="cid:screen" /></a></p>
<ul>
<li>list</li>
<li>of</li>
<li>stuff</li>
</ul>

You can see that I have an image wrapped in <a> tags.

For the python script, I thought that using the dictionary for attaching an image would help to make this work:

subject = 'test'
img = {'screen.png': 'screen'}
yag.send(to = '', subject=subject, contents = [html, img])

As it is, the image doesn't actually get attached. In my email client it is just showing the dictionary itself.

Question 1: is the alias provided in a dictionary something that I could use to reference an image in html? If so, how can that be done?

You can see that I've tried to do so by sourcing the image as cid:screen using the alias from the dictionary.

Question 2: Why isn't the image being properly attached? Am I doing it wrong?

Question 3: If there's no built in way to do this, are there plans to do so?

Other solution: I know that I could potentially chop up my html and use the .inline() function to get this working. I haven't tried that yet though. In theory it should work. EDIT: I tried this - it's buggy. See next comment.

Thanks for your time!

1dancook commented 3 years ago

I've tested the html chop-up theory. It doesn't work. I haven't checked how yagmail deals with html internally, but the result is rather buggy for HTML. (You suggested this method in #150 as well that was untested ... here are the results)

Original HTML:

<h1>Test Title</h1>
<p><a href="https://www.google.com">a normal link to google</a></p>
<p><a href="https://www.google.com"><img alt="image" src="screen.png" /></a></p>
<p><img alt="image of the screenshot" src="screen.png" /></p>

I ran this script to split up the HTML and replace any image references (if its a valid local path) to yagmail.inline():

img_regex = '\<img.*?\/\>'
image_tags = re.findall(img_regex, html)
html_parts = re.split(img_regex, html)

contents = []
for x, part in enumerate(html_parts):
  contents.append(part)
  try:
    tag = image_tags[x]
    src_regex ='(?<=src=").*?(?=")'
    src = re.search(src_regex, tag).group(0)
    if os.path.exists(src):
      contents.append(yagmail.inline(src))
    else:
      contents.append(tag)
  except:
    pass

That gives me this result for contents:

['<h1>Test Title</h1>\n<p><a href="https://www.google.com">a normal link to google</a></p>\n<p><a href="https://www.google.com">', 'screen.png', '</a></p>\n<p>', 'screen.png', '</p>']

Now, after sending the email, the resulting HTML (decoded from base64) is this:

<html>

<head></head>

<body>
  <div>
    <h1>Test Title</h1><br>
    <p><a href="https://www.google.com">a normal link to google</a></p><br>
    <p><a href="https://www.google.com"></a></p>
  </div>
</body><img src="cid:8452894641350184503" title="screen.png">
<div><br>
  <p></p>
</div>
<html><img src="cid:8452894641350184503" title="screen.png">
<div></div>

</html>

</html>

My guess to what is happening internally is that for each HTML content part, yagmail is trying to process/wrap it in some way, and then after that join them all together. There are some inconsistencies with that HTML output (i.e. - one section has a and the other doesn't... etc.) -- so I'm not sure exactly what yagmail is trying to do.

If it is processing each HTML part individually, I would suggest that first all parts are joined together and then processed as one.

Another issue altogether is that the resulting HTML containts extra elements that don't need to be there.

Somewhat related: is it possible to get the resulting email body/html from yagmail without actually sending the email (would make it easier to debug something like this)