1 /** 2 Copyright: © 2014 rejectedsoftware e.K. 3 License: Subject to the terms of the GNU GPLv3 license, as written in the included LICENSE.txt file. 4 Authors: Sönke Ludwig 5 */ 6 module dubregistry.notificationcenter; 7 8 import dubregistry.scheduler; 9 10 import std.array : appender; 11 import std.datetime; 12 import std.encoding : sanitize; 13 import std.string : format; 14 import userman.db.controller; 15 import vibe.core.log; 16 import vibe.mail.smtp; 17 import vibe.stream.memory; 18 import diet.html; 19 20 21 22 class NotificationCenter { 23 @safe: 24 25 private { 26 UserManController m_users; 27 PersistentScheduler m_scheduler; 28 string[][string][User.ID] m_deprecations; 29 30 enum weeklyDeprecationEventName = "deprecation-warnings"; 31 } 32 33 this(UserManController users) 34 { 35 m_users = users; 36 m_scheduler = new PersistentScheduler(NativePath("notification-schedule.json")); 37 if (!m_scheduler.existsEvent(weeklyDeprecationEventName)) 38 m_scheduler.scheduleWeeklyEvent(weeklyDeprecationEventName, SysTime(DateTime(2014, 01, 05, 12, 0, 0), UTC())); 39 } 40 41 void startRegularNotifications() 42 { 43 m_scheduler.setEventHandler(weeklyDeprecationEventName, &onSendDeprecationWarnings); 44 } 45 46 void notifyNewErrors(User.ID user_id, string package_name, string branch_or_version, string[] errors) 47 @trusted { 48 auto user = m_users.getUser(user_id); 49 if (user.email != "sludwig@rejectedsoftware.com") return; 50 51 auto settings = m_users.settings; 52 53 auto mail = new Mail; 54 mail.headers["From"] = format("%s <%s>", settings.serviceName, settings.serviceEmail); 55 mail.headers["To"] = format("%s <%s>", user.fullName, user.email); // FIXME: sanitize/escape user.fullName 56 mail.headers["Subject"] = format("[%s] Errors in new version %s", package_name, branch_or_version); 57 58 auto dst = appender!string(); 59 dst.compileHTMLDietFile!("dubregistry.mail.package-version-errors.dt", user, settings, package_name, branch_or_version, errors); 60 mail.bodyText = dst.data; 61 62 sendMail(settings.mailSettings, mail); 63 } 64 65 void setDeprecationWarnings(User.ID user_id, string package_name, string[] warnings) 66 { 67 auto pd = user_id in m_deprecations; 68 if (!pd) { 69 m_deprecations[user_id] = null; 70 pd = user_id in m_deprecations; 71 } 72 if (warnings.length) (*pd)[package_name] = warnings; 73 else if (package_name in *pd) (*pd).remove(package_name); 74 } 75 76 private void onSendDeprecationWarnings() 77 nothrow @trusted { 78 foreach (uid_deprecations; m_deprecations.byKeyValue) { 79 auto uid = uid_deprecations.key; 80 auto deprecations = uid_deprecations.value; 81 User user; 82 try { 83 user = m_users.getUser(uid); 84 if (user.email != "sludwig@rejectedsoftware.com") continue; 85 86 auto settings = m_users.settings; 87 88 auto mail = new Mail; 89 mail.headers["From"] = format("%s <%s>", settings.serviceName, settings.serviceEmail); 90 mail.headers["To"] = format("%s <%s>", user.fullName, user.email); // FIXME: sanitize/escape user.fullName 91 mail.headers["Subject"] = "Weekly deprecation warnings reminder"; 92 93 auto dst = appender!string(); 94 dst.compileHTMLDietFile!("dubregistry.mail.package-deprecation-warnings.dt", user, settings, deprecations); 95 mail.bodyText = cast(string)dst.data; 96 97 sendMail(settings.mailSettings, mail); 98 } catch (Exception e) { 99 logDiagnostic("Failed to send deprecation mail to %s <%s>: %s", user.fullName, user.email, e.msg); 100 logDebug("Full error: %s", e.toString().sanitize); 101 } 102 } 103 } 104 }