-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapplicationEntry.d
218 lines (190 loc) · 6.24 KB
/
applicationEntry.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import std.conv;
import std.file;
import std.stdio;
import std.string;
import std.regex;
import core.time : msecs;
import vibe.core.args;
import vibe.core.concurrency;
import vibe.core.core;
import vibe.core.log;
import vibe.http.client;
import vibe.http.fileserver;
import vibe.http.router;
import vibe.http.server;
import vibe.stream.operations : readAllUTF8;
import vibe.web.rest;
import activation_list;
import app;
import chat_collection;
import common_types;
import config;
import telegram_api;
__gshared Task g_commandProcessor;
__gshared Task g_telegramUpdater;
__gshared Config g_config;
enum ActivationTag {_};
enum ChatTag {_};
enum TestTag {_};
immutable string TELEGRAM_BOT_URL = "https://api.telegram.org/bot";
void runCommandProcessor() {
static class TemporaryChatServer : ChatServer {
void sendMessage(ChatId chat, string message) {
string baseUrl = TELEGRAM_BOT_URL ~ g_config.appToken;
requestHTTP(format("%s/sendMessage?chat_id=%d&text=%s", baseUrl, chat, message),
(scope request) {},
(scope response) {
// TODO check response
logInfo("Telegram response: %s", new JsonResponseImpl(response).rawResponse());
}
);
logInfo("ChatServer.sendMessage: id = %d, message = %s", chat, message);
}
}
auto chatCollection = createChatCollection(new TemporaryChatServer);
auto activationList = createActivationList(chatCollection);
g_commandProcessor = runTask({
while (true) {
receive(
(ActivationTag _, Task target) {
target.send(activationList.getActivationList());
},
(ActivationTag _, Task target, string chatName) {
target.send(activationList.postActivationList(chatName));
},
(ActivationTag _, string chatName, uint code, int chatId) {
activationList.activateChat(chatName, code, chatId);
},
(ChatTag _, Task target) {
target.send(chatCollection.getChatList());
},
(TestTag _, Task target, string chatName, uint code, int chatId) {
target.send(activationList.activateChat(chatName, code, chatId));
},
(TestTag _, string chatToken, string message) {
chatCollection.sendMessage(chatToken, message);
});
}
});
runEventLoop();
}
void runTelegramUpdater() {
g_telegramUpdater = runTask({
string updateUrl = TELEGRAM_BOT_URL ~ g_config.appToken ~ "/getUpdates?offset=";
int maxId = -1;
auto activationFilter = ctRegex!(q"{^\/start\s+(\d{6,6})\s*$}");
while (true) {
requestHTTP(updateUrl ~ to!string(maxId + 1),
(scope request) {},
(scope response) {
try {
foreach (update; getTelegramUpdates(new JsonResponseImpl(response))) {
int id = update.id();
if (maxId < id) {
maxId = id;
}
auto message = update.message();
auto from = message.from();
auto chat = message.chat();
auto text = message.text();
logInfo("Chat \"%s\": \"%s\" wrote \"%s\"", chat.chatName(), from.chatName(), text);
auto activationMatch = text.match(activationFilter);
if (!activationMatch.empty) {
uint code = activationMatch.captures[1].to!uint;
logInfo("Activation code received: %d", code);
g_commandProcessor.send(ActivationTag._, chat.chatName(), code, chat.chatId());
}
}
} catch (Exception error) {
logError("Error: %s", error.msg);
}
}
);
sleep(1000.msecs);
}
});
runEventLoop();
}
class JsonResponseImpl : JsonResponse {
this(HTTPClientResponse response) {
response_ = response;
}
int statusCode() const {
return response_.statusCode;
}
string rawResponse() {
return response_.bodyReader.readAllUTF8();
}
Json json() {
return response_.readJson();
}
private HTTPClientResponse response_;
}
class ActivationRestInterfaceImpl : ActivationRestInterface {
ActivationEntries getActivationList() {
g_commandProcessor.send(ActivationTag._, Task.getThis());
return receiveOnly!ActivationEntries;
}
ActivationEntry postActivationList(string chatName) {
g_commandProcessor.send(ActivationTag._, Task.getThis(), chatName);
return receiveOnly!ActivationEntry;
}
}
class ChatRestInterfaceImpl : ChatRestInterface {
ChatList getChatList() {
g_commandProcessor.send(ChatTag._, Task.getThis());
return receiveOnly!ChatList;
}
}
interface TestInterface {
bool postActivationCode(string chatName, uint code, int chatId);
bool postTelegramMessage(string token, string message);
}
class TestInterfaceImpl : TestInterface {
bool postActivationCode(string chatName, uint code, int chatId) {
g_commandProcessor.send(TestTag._, Task.getThis(), chatName, code, chatId);
return receiveOnly!bool;
}
bool postTelegramMessage(string token, string message) {
g_commandProcessor.send(TestTag._, token, message);
// FIXME return real result
return true;
}
}
void showActivationView(HTTPServerRequest request, HTTPServerResponse response) {
response.render!("activation.dt", request);
}
shared static this() {
string json;
try {
json = readText("teleherald.json");
} catch(Exception e) {
logError("Cannot read config file. Error: %s", e.msg);
}
g_config = parseConfig(json);
new core.thread.Thread(&runCommandProcessor).start();
new core.thread.Thread(&runTelegramUpdater).start();
string basePath = "/" ~ g_config.activatorPath();
string restBasePath = basePath ~ "/";
auto router = new URLRouter;
router.get(basePath, &showActivationView);
router.registerRestInterface(new ActivationRestInterfaceImpl, restBasePath, MethodStyle.camelCase);
router.registerRestInterface(new ChatRestInterfaceImpl, restBasePath, MethodStyle.camelCase);
router.registerRestInterface(new TestInterfaceImpl, restBasePath, MethodStyle.camelCase);
router.get("*", serveStaticFiles("./public/"));
auto settings = new HTTPServerSettings;
settings.port = 8080;
listenHTTP(settings, router);
}
class RealApp : App {
void start() {
if (!finalizeCommandLineOptions())
return;
lowerPrivileges();
runEventLoop();
}
}
void main()
{
startApp(new RealApp);
}