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 }