LoginSignup
1

Office365からRedmineへのメール送信してチケット登録

Last updated at Posted at 2023-05-26

ハマったこと

この3日間悩みまくりました。
結論から言うと、メールソフトはメールサーバーで扱え・・・

コード

最初はPOSTでFASTAPIに送ろうとしてたけど、エラー404 not foundが消えず。

もう半ばあきらめた時に、とっさに閃いたのは、結局は前回と同じで
pythonのメールサーバーに送ればいいんじゃない?

方針変えてからコードが動くまでは、あっと言う間でした。

cs.cs
using System;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Outlook;
using System.Net.Mail;
using Attachment = Microsoft.Office.Interop.Outlook.Attachment;

namespace SampleRibbonOutlookAddIn
{
    [ComVisible(true)]
    public class Ribbon : IRibbonExtensibility
    {
        string REDMINE = "redmine@admin.com";

        public void onMyButton_Click(IRibbonControl control)
        {
            Microsoft.Office.Interop.Outlook.MailItem mailItem = GetSelectedMailItem();
            // Prompt the user to confirm sending to Redmine
            DialogResult result = MessageBox.Show(
                 mailItem.Subject, "Send to Redmine?",
                    MessageBoxButtons.YesNo,
                MessageBoxIcon.Question
            );

            if (result == DialogResult.Yes)   {
                if (mailItem != null)                {
                    // Call the Redmine API function with the email subject as the issue title
                    Post_message3(mailItem);
                }else
                { MessageBox.Show("No MailItem selected!");}
            }
        }

        private void Post_message3(MailItem outlookMessage )   {
            MailMessage message = new MailMessage();
            message.To.Add(new MailAddress(outlookMessage.SenderEmailAddress));
            message.Subject = outlookMessage.Subject;
            message.Body = outlookMessage.Body;
            message.From = new MailAddress(REDMINE);
            foreach (Microsoft.Office.Interop.Outlook.Attachment attachment in outlookMessage.Attachments)
            {
                string displayName = attachment.DisplayName;
                int index = displayName.LastIndexOf('.');
                string extension = index > -1 ? displayName.Substring(index) : string.Empty;
                // Get the MIME content type of the attachment
                string contentType = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x370E001F");
                message.Attachments.Add(new System.Net.Mail.Attachment(attachment.PathName, contentType));
            }
            // Create a new SmtpClient object
            SmtpClient client = new SmtpClient("172.26.106.79", 1025);
            client.UseDefaultCredentials = false;
            client.Send(message);
        }
    }
}

メール受信(python)

だいぶ、苦労したけど動きました。

ハマったこと

smtpのメッセージから、メール添付ファイルの取り出し(全部、CHATGPTに書かせた)

課題

メールの送信者のアドレスで、redmineに都度ログインする部分が出来ていない
→ まずはadminのTOKENでログインしている

python.py

import smtpd
import asyncore
import email
import os
from email.header import decode_header
from redminelib import Redmine
import pdb
from datetime import datetime, timedelta

SAVE_DIR = "./store"

def save_attachment(msg, save_dir):
    filepaths = []
    for part in msg.walk():
        if part.get_content_maintype() == 'multipart':
            continue
        if part.get('Content-Disposition') is None:
            continue

        filename = part.get_filename()
        if filename:
            # Decode the filename if it's encoded
            decoded_filename = decode_header(filename)[0][0]
            if isinstance(decoded_filename, bytes):
                # Handle bytes strings
                decoded_filename = decoded_filename.decode()

            # Remove any invalid characters from the filename
            cleaned_filename = ''.join(c for c in decoded_filename if c.isalnum() or c in ['.', '_', '-'])

            save_path = os.path.join(save_dir, cleaned_filename)
            with open(save_path, 'wb') as f:
                f.write(part.get_payload(decode=True))

            print(f"Saved attachment: {cleaned_filename}")
            filepaths.append(save_path)

    print("All attachments saved successfully.")
    return filepaths

# Define the function for parsing the email message
def mail_parse(msg):
    email_data = {}
    email_data['From'] = msg['from']
    email_data['To'] = msg['to']
    email_data['Subject'] = msg['subject']
    email_data['Body']=msg['body']

    # Extract body and add to email_data
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            body = part.get_payload(decode=True).decode()
            email_data['Body'] = body

    # Save attachments and add their filepaths to email_data
    if msg.get_content_maintype() == 'multipart':
        files = save_attachment(msg, SAVE_DIR)
        email_data['Attachments'] = files

    return email_data

class RedmineWork:
    def __init__(self, url):
        self.url = url
        
    def api_login(self, api_key):
        self.api = Redmine(self.url, key=api_key) 

    def get_all_pjt(self):
        return self.api.project.all()

    def push_ticket(self,msg,files):
        projects = self.get_all_pjt()
        for project in projects:
            print(project.name)
        issue = self.api.issue.new()
# 日付を設定
        start_date = datetime.now().date()
        issue.start_date = start_date
        issue.due_date = start_date + timedelta(days=1) # Add one day to due date

        issue.project_id = projects[0].id 
        issue.subject = msg["Subject"]
        issue.description = ""
        issue.tracker_id = 1#新規
        issue.status_id = 1
        issue.priority_id = 1
        issue.assigned_to_id = 1
        files_up = [{'path': file_path,'filename': os.path.basename(file_path)} for file_path in files]
        issue.uploads = files_up

        issue.save()

class CustomSMTPServer(smtpd.SMTPServer):
    def __init__(self, localaddr, remoteaddr, redmine, **kwargs):
        self.redmine = redmine
        super().__init__(localaddr, remoteaddr, **kwargs)

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        msg = email.message_from_bytes(data)
        files = save_attachment(msg, SAVE_DIR)
        self.redmine.push_ticket(msg,files)

def start_server(url, api_key):
    redmine = RedmineWork(url)
    redmine.api_login(api_key)

    # Create an instance of the custom SMTP server with redmine passed as argument
    server = CustomSMTPServer(('localhost', 1025), None, redmine)
    asyncore.loop()

if __name__ == "__main__":
    url = r"http://redmine:8000"
    api_key = "9e9c6a437feffbd06dc070e348f60e9eb1b7f027"

    start_server(url, api_key)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1