1: <?php
2:
3: namespace Budabot\Core;
4:
5: use stdClass;
6: use Exception;
7:
8: 9: 10:
11: class EventManager {
12:
13:
14: public $db;
15:
16:
17: public $chatBot;
18:
19:
20: public $settingManager;
21:
22:
23: public $util;
24:
25:
26: public $logger;
27:
28: public $events = array();
29: private $cronevents = array();
30:
31: private $eventTypes = array(
32: 'msg','priv','extpriv','guild','joinpriv','leavepriv',
33: 'orgmsg','extjoinprivrequest','logon','logoff','towers',
34: 'connect','setup'
35: );
36:
37: private $lastCronTime = 0;
38: private $areConnectEventsFired = false;
39: const PACKET_TYPE_REGEX = '/packet\(\d+\)/';
40: const TIMER_EVENT_REGEX = '/timer\(([0-9a-z]+)\)/';
41:
42: 43: 44: 45:
46: public function register($module, $type, $filename, $description = 'none', $help = '', $defaultStatus = null) {
47: $type = strtolower($type);
48:
49: $this->logger->log('DEBUG', "Registering event Type:($type) Handler:($filename) Module:($module)");
50:
51: if (!$this->isValidEventType($type) && $this->getTimerEventTime($type) == 0) {
52: $this->logger->log('ERROR', "Error registering event Type:($type) Handler:($filename) Module:($module). The type is not a recognized event type!");
53: return;
54: }
55:
56: list($name, $method) = explode(".", $filename);
57: if (!Registry::instanceExists($name)) {
58: $this->logger->log('ERROR', "Error registering method $filename for event type $type. Could not find instance '$name'.");
59: return;
60: }
61:
62: try {
63: if (isset($this->chatBot->existing_events[$type][$filename])) {
64: $sql = "UPDATE eventcfg_<myname> SET `verify` = 1, `description` = ?, `help` = ? WHERE `type` = ? AND `file` = ? AND `module` = ?";
65: $this->db->exec($sql, $description, $help, $type, $filename, $module);
66: } else {
67: if ($defaultStatus === null) {
68: if ($this->chatBot->vars['default_module_status'] == 1) {
69: $status = 1;
70: } else {
71: $status = 0;
72: }
73: } else {
74: $status = $defaultStatus;
75: }
76: $sql = "INSERT INTO eventcfg_<myname> (`module`, `type`, `file`, `verify`, `description`, `status`, `help`) VALUES (?, ?, ?, ?, ?, ?, ?)";
77: $this->db->exec($sql, $module, $type, $filename, '1', $description, $status, $help);
78: }
79: } catch (SQLException $e) {
80: $this->logger->log('ERROR', "Error registering method $filename for event type $type: " . $e->getMessage());
81: }
82: }
83:
84: 85: 86: 87:
88: public function activate($type, $filename) {
89: $type = strtolower($type);
90:
91: $this->logger->log('DEBUG', "Activating event Type:($type) Handler:($filename)");
92:
93: list($name, $method) = explode(".", $filename);
94: if (!Registry::instanceExists($name)) {
95: $this->logger->log('ERROR', "Error activating method $filename for event type $type. Could not find instance '$name'.");
96: return;
97: }
98:
99: if ($type == "setup") {
100: $eventObj = new stdClass;
101: $eventObj->type = 'setup';
102:
103: $this->callEventHandler($eventObj, $filename);
104: } else if ($this->isValidEventType($type)) {
105: if (!isset($this->events[$type]) || !in_array($filename, $this->events[$type])) {
106: $this->events[$type] []= $filename;
107: } else {
108: $this->logger->log('ERROR', "Error activating event Type:($type) Handler:($filename). Event already activated!");
109: }
110: } else {
111: $time = $this->getTimerEventTime($type);
112: if ($time > 0) {
113: $key = $this->getKeyForCronEvent($time, $filename);
114: if ($key === null) {
115: $this->cronevents[] = array('nextevent' => 0, 'filename' => $filename, 'time' => $time);
116: } else {
117: $this->logger->log('ERROR', "Error activating event Type:($type) Handler:($filename). Event already activated!");
118: }
119: } else {
120: $this->logger->log('ERROR', "Error activating event Type:($type) Handler:($filename). The type is not a recognized event type!");
121: }
122: }
123: }
124:
125: 126: 127: 128:
129: public function deactivate($type, $filename) {
130: $type = strtolower($type);
131:
132: $this->logger->log('debug', "Deactivating event Type:($type) Handler:($filename)");
133:
134: if ($this->isValidEventType($type)) {
135: if (in_array($filename, $this->events[$type])) {
136: $found = true;
137: $temp = array_flip($this->events[$type]);
138: unset($this->events[$type][$temp[$filename]]);
139: }
140: } else {
141: $time = $this->getTimerEventTime($type);
142: if ($time > 0) {
143: $key = $this->getKeyForCronEvent($time, $filename);
144: if ($key != null) {
145: $found = true;
146: unset($this->cronevents[$key]);
147: }
148: } else {
149: $this->logger->log('ERROR', "Error deactivating event Type:($type) Handler:($filename). The type is not a recognized event type!");
150: return;
151: }
152: }
153:
154: if (!$found) {
155: $this->logger->log('ERROR', "Error deactivating event Type:($type) Handler:($filename). The event is not active or doesn't exist!");
156: }
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166:
167: public function activateIfDeactivated($obj) {
168: $eventMethods = func_get_args();
169: array_shift($eventMethods);
170: forEach ($eventMethods as $eventMethod) {
171: $call = Registry::formatName(get_class($obj)) . "." . $eventMethod;
172: $type = $this->getEventTypeByMethod($obj, $eventMethod);
173: if ($type !== null) {
174: if (isset($this->events[$type]) && in_array($call, $this->events[$type])) {
175:
176: continue;
177: }
178: $this->activate($type, $call);
179: } else {
180: $this->logger->log('ERROR', "Could not find event for '$call'");
181: }
182: }
183: }
184:
185: 186: 187: 188: 189: 190: 191: 192:
193: public function deactivateIfActivated($obj) {
194: $eventMethods = func_get_args();
195: array_shift($eventMethods);
196: forEach ($eventMethods as $eventMethod) {
197: $call = Registry::formatName(get_class($obj)) . "." . $eventMethod;
198: $type = $this->getEventTypeByMethod($obj, $eventMethod);
199: if ($type !== null) {
200: if (!isset($this->events[$type]) || !in_array($call, $this->events[$type])) {
201:
202: continue;
203: }
204: $this->deactivate($type, $call);
205: } else {
206: $this->logger->log('ERROR', "Could not find event for '$call'");
207: }
208: }
209: }
210:
211: public function getEventTypeByMethod($obj, $methodName) {
212: $method = new ReflectionAnnotatedMethod($obj, $methodName);
213: if ($method->hasAnnotation('Event')) {
214: return strtolower($method->getAnnotation('Event')->value);
215: } else {
216: return null;
217: }
218: }
219:
220: public function getKeyForCronEvent($time, $filename) {
221: forEach ($this->cronevents as $key => $event) {
222: if ($time == $event['time'] && $event['filename'] == $filename) {
223: return $key;
224: }
225: }
226: return null;
227: }
228:
229: 230: 231: 232:
233: public function loadEvents() {
234: $this->logger->log('DEBUG', "Loading enabled events");
235:
236: $data = $this->db->query("SELECT * FROM eventcfg_<myname> WHERE `status` = '1'");
237: forEach ($data as $row) {
238: $this->activate($row->type, $row->file);
239: }
240: }
241:
242: 243: 244: 245:
246: public function crons() {
247: $time = time();
248:
249: if ($this->lastCronTime == $time) {
250: return;
251: }
252: $this->lastCronTime = $time;
253:
254: $this->logger->log('DEBUG', "Executing cron events at '$time'");
255: forEach ($this->cronevents as $key => $event) {
256: if ($this->cronevents[$key]['nextevent'] <= $time) {
257: $this->logger->log('DEBUG', "Executing cron event '${event['filename']}'");
258:
259: $eventObj = new stdClass;
260: $eventObj->type = strtolower($event['time']);
261:
262: $this->cronevents[$key]['nextevent'] = $time + $event['time'];
263: $this->callEventHandler($eventObj, $event['filename']);
264: }
265: }
266: }
267:
268: 269: 270: 271:
272: public function executeConnectEvents() {
273:
274: if ($this->areConnectEventsFired) {
275: return;
276: }
277: $this->areConnectEventsFired = true;
278:
279: $this->logger->log('DEBUG', "Executing connected events");
280:
281: $eventObj = new stdClass;
282: $eventObj->type = 'connect';
283:
284: $this->fireEvent($eventObj);
285: }
286:
287: public function isValidEventType($type) {
288: return (in_array($type, $this->eventTypes) || preg_match(self::PACKET_TYPE_REGEX, $type) == 1);
289: }
290:
291: public function getTimerEventTime($type) {
292: if (preg_match(self::TIMER_EVENT_REGEX, $type, $arr) == 1) {
293: $time = $this->util->parseTime($arr[1]);
294: if ($time > 0) {
295: return $time;
296: }
297: } else {
298: $time = $this->util->parseTime($type);
299: if ($time > 0) {
300: return $time;
301: }
302: }
303: return 0;
304: }
305:
306: public function fireEvent($eventObj) {
307: if (isset($this->events[$eventObj->type])) {
308: forEach ($this->events[$eventObj->type] as $filename) {
309: $this->callEventHandler($eventObj, $filename);
310: }
311: }
312: }
313:
314: public function callEventHandler($eventObj, $handler) {
315: $this->logger->log('DEBUG', "Executing handler '$handler' for event type '$eventObj->type'");
316:
317: try {
318: list($name, $method) = explode(".", $handler);
319: $instance = Registry::getInstance($name);
320: if ($instance === null) {
321: $this->logger->log('ERROR', "Could not find instance for name '$name' in '$handler' for event type '$eventObj->type'");
322: } else {
323: $instance->$method($eventObj);
324: }
325: } catch (StopExecutionException $e) {
326: throw $e;
327: } catch (Exception $e) {
328: $this->logger->log('ERROR', "Error calling event handler '$handler': " . $e->getMessage(), $e);
329: }
330: }
331:
332: public function addEventType($eventType) {
333: $eventType = strtolower($eventType);
334:
335: if (in_array($eventType, $this->eventTypes)) {
336: $this->logger->log('WARN', "Event type already registered: '$eventType'");
337: return false;
338: } else {
339: $this->eventTypes []= $eventType;
340: return true;
341: }
342: }
343: }
344: