Skip to content

Commit 0b8fa49

Browse files
authored
Merge pull request #376 from Jurigag/3.x
Added discord integration
2 parents 0d7199e + bd79e00 commit 0b8fa49

File tree

14 files changed

+421
-3584
lines changed

14 files changed

+421
-3584
lines changed

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,10 @@ LOGGER_LEVEL=debug
8484
GOOGLE_ANALYTICS=
8585

8686
SOCIAL_TWITTER_NAME=phalconphp
87+
88+
DISCORD_BOT_TOKEN=
89+
DISCORD_MESSAGE_ABOUT_NEW_DISCUSSIONS=false
90+
DISCORD_MESSAGE_ABOUT_REPLIES=false
91+
DISCORD_MESSAGE_ABOUT_SOLVED_DISCUSSIONS=false
92+
DISCORD_CHANNEL_ID=
93+
DISCORD_GUILD_ID=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
.env
1010
/public/robots.txt
1111
/public/sitemap.xml
12+
!.env.example

app/config/discord.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
+------------------------------------------------------------------------+
5+
| Phosphorum |
6+
+------------------------------------------------------------------------+
7+
| Copyright (c) 2013-2016 Phalcon Team and contributors |
8+
+------------------------------------------------------------------------+
9+
| This source file is subject to the New BSD License that is bundled |
10+
| with this package in the file LICENSE.txt. |
11+
| |
12+
| If you did not receive a copy of the license and are unable to |
13+
| obtain it through the world-wide-web, please send an email |
14+
| to license@phalconphp.com so we can send you a copy immediately. |
15+
+------------------------------------------------------------------------+
16+
*/
17+
18+
return [
19+
'token_bot' => env('DISCORD_BOT_TOKEN', null),
20+
'message' => [
21+
'new_discussions' => env('DISCORD_MESSAGE_ABOUT_NEW_DISCUSSIONS', false),
22+
'new_replies' => env('DISCORD_MESSAGE_ABOUT_REPLIES', false),
23+
'solved_discussions' => env('DISCORD_MESSAGE_ABOUT_SOLVED_DISCUSSIONS', false),
24+
],
25+
'channel_id' => env('DISCORD_CHANNEL_ID', null),
26+
'guild_id' => env('DISCORD_GUILD_ID', null),
27+
];

app/config/providers.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
Phosphorum\Provider\Email\ServiceProvider::class,
4646
Phosphorum\Provider\Mailer\ServiceProvider::class,
4747
Phosphorum\Provider\Assets\ServiceProvider::class,
48+
Phosphorum\Provider\Discord\ServiceProvider::class,
4849

4950
// Third Party Providers
5051
// ...
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
3+
namespace Phosphorum\Discord;
4+
5+
use Exception;
6+
use Phalcon\Di\Injectable;
7+
use Phalcon\Http\Client\Provider\Curl;
8+
use Phalcon\Http\Client\Request;
9+
use Phosphorum\Model\Posts;
10+
use Phosphorum\Model\PostsReplies;
11+
use Phosphorum\Model\Services\Service\Posts as PostsService;
12+
13+
/**
14+
* Class DiscordComponent
15+
*
16+
* @property \Phalcon\Queue\Beanstalk $queue
17+
* @package Phosphorum\Discord
18+
*/
19+
class DiscordComponent extends Injectable
20+
{
21+
/**
22+
* @var array
23+
*/
24+
protected $options;
25+
26+
/**
27+
* Creates component
28+
*
29+
* @param $options
30+
*/
31+
public function __construct($options)
32+
{
33+
$this->options = $options;
34+
}
35+
36+
/**
37+
* @return mixed
38+
*/
39+
public function getToken()
40+
{
41+
return $this->options['token_bot'];
42+
}
43+
44+
/**
45+
* @return mixed
46+
*/
47+
public function getChannelId()
48+
{
49+
return $this->options['channel_id'];
50+
}
51+
52+
/**
53+
* @return mixed
54+
*/
55+
public function getGuildId()
56+
{
57+
return $this->options['guild_id'];
58+
}
59+
60+
/**
61+
* @return bool
62+
*/
63+
public function isMessagingAboutNewDiscussion()
64+
{
65+
return $this->options['message']['new_discussions'] == true;
66+
}
67+
68+
/**
69+
* @return bool
70+
*/
71+
public function isMessagingAboutReplies()
72+
{
73+
return $this->options['message']['new_replies'] == true;
74+
}
75+
76+
/**
77+
* @return bool
78+
*/
79+
public function isMessagingAboutSolvedDiscussion()
80+
{
81+
return $this->options['message']['solved_discussions'] == true;
82+
}
83+
84+
/**
85+
* @param Posts $posts
86+
*/
87+
public function addMessageAboutDiscussion(Posts $posts)
88+
{
89+
if ($this->isMessagingAboutNewDiscussion()) {
90+
/** @var PostsService $postService */
91+
$postService = container(PostsService::class);
92+
93+
$href = $postService->getPostUrl($posts);
94+
$message = sprintf(
95+
":newspaper2: **New discussion `%s` was started by `%s`. Check it out on %s.**",
96+
$posts->title,
97+
$posts->user->name,
98+
$href
99+
);
100+
$embed = [
101+
'title' => $posts->title,
102+
'description' => $this->makeDescription($posts->content, 375),
103+
'url' => $href,
104+
'timestamp' => $posts->getUTCModifiedAt(),
105+
'type' => 'rich',
106+
'author' => [
107+
'name' => $posts->user->name,
108+
'url' => container('config')->site->url . "/user/{$posts->users_id}/{$posts->user->login}",
109+
'icon_url' => $this->gravatar->setSize(220)->getAvatar($posts->user->email),
110+
],
111+
];
112+
$this->queue->choose('discord');
113+
$this->queue->put(
114+
[
115+
'message' => $message,
116+
'embed' => $embed,
117+
]
118+
);
119+
}
120+
}
121+
122+
/**
123+
* @param PostsReplies $reply
124+
*/
125+
public function addMessageAboutReply(PostsReplies $reply)
126+
{
127+
if ($this->isMessagingAboutReplies()) {
128+
$postService = container(PostsService::class);
129+
130+
$href = $postService->getPostUrl($reply->post);
131+
$message = sprintf(
132+
"\":newspaper2: **`%s` has commented in discussion `%s`. Check it out on %s#C%s.**",
133+
$reply->user->name,
134+
$reply->post->title,
135+
$href,
136+
$reply->id
137+
);
138+
$embed = [
139+
'title' => $reply->post->title,
140+
'description' => $this->makeDescription($reply->content, 375),
141+
'url' => "$href#C{$reply->id}",
142+
'timestamp' => $reply->getUTCCreatedAt(),
143+
'type' => 'rich',
144+
'author' => [
145+
'name' => $reply->user->name,
146+
'url' => container('config')->site->url . "/user/{$reply->users_id}/{$reply->user->login}",
147+
'icon_url' => $this->gravatar->setSize(220)->getAvatar($reply->user->email),
148+
],
149+
];
150+
$this->queue->choose('discord');
151+
$this->queue->put(
152+
[
153+
'message' => $message,
154+
'embed' => $embed,
155+
]
156+
);
157+
}
158+
}
159+
160+
/**
161+
* @param PostsReplies $reply
162+
*/
163+
public function addMessageAboutSolvedDiscussion(PostsReplies $reply)
164+
{
165+
if ($this->isMessagingAboutSolvedDiscussion()) {
166+
$postService = container(PostsService::class);
167+
168+
$href = $postService->getPostUrl($reply->post);
169+
$message = sprintf(
170+
":newspaper2: **Discussion `%s` was marked as solved. Check out accepted answer on %s#C%s.**",
171+
$reply->post->title,
172+
$href,
173+
$reply->id
174+
);
175+
$embed = [
176+
'title' => $reply->post->title,
177+
'description' => $this->makeDescription($reply->content, 375),
178+
'url' => "$href#C{$reply->id}",
179+
'timestamp' => $reply->getUTCCreatedAt(),
180+
'type' => 'rich',
181+
'author' => [
182+
'name' => $reply->user->name,
183+
'url' => container('config')->site->url . "/user/{$reply->users_id}/{$reply->user->login}",
184+
'icon_url' => $this->gravatar->setSize(220)->getAvatar($reply->user->email),
185+
],
186+
];
187+
$this->queue->choose('discord');
188+
$this->queue->put(
189+
[
190+
'message' => $message,
191+
'embed' => $embed,
192+
]
193+
);
194+
}
195+
}
196+
197+
/**
198+
* @param $description
199+
* @param $toLength
200+
* @return mixed|string
201+
*/
202+
protected function makeDescription($description, $toLength)
203+
{
204+
$description = str_replace(["\n", "\r", "\t"], ' ', $description);
205+
$description = trim($description);
206+
$padding = substr($description, $toLength);
207+
if ($padding === 0) {
208+
return $description;
209+
}
210+
$lengthDot = strpos($padding, ".");
211+
if ($lengthDot === 0) {
212+
return $description;
213+
}
214+
$lengthSpace = strpos($padding, " ");
215+
if ($lengthSpace === 0) {
216+
return $description;
217+
}
218+
219+
return substr($description, 0, min($lengthDot + $toLength + 1, $lengthSpace + $toLength)) . "...";
220+
}
221+
}

app/library/Mail/SendSpool.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ public function sendRemaining()
5050
*/
5151
public function consumeQueue()
5252
{
53+
$queue = container('queue');
54+
$queue->watch('notifications');
5355
while (true) {
54-
while (container('queue')->peekReady() !== false) {
55-
$job = container('queue')->queue->reserve();
56-
56+
while ($job = $queue->reserve() !== false) {
5757
$message = $job->getBody();
5858

5959
foreach ($message as $userId => $id) {

app/model/Posts.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use Phalcon\Diff\Renderer\Html\SideBySide;
2626
use Phalcon\Mvc\Model\Behavior\SoftDelete;
2727
use Phalcon\Mvc\Model\Behavior\Timestampable;
28+
use Phalcon\Queue\Beanstalk;
29+
use Phosphorum\Discord\DiscordComponent;
2830

2931
/**
3032
* Class Posts
@@ -162,6 +164,8 @@ public function initialize()
162164
]
163165
);
164166

167+
$this->keepSnapshots(true);
168+
165169
$this->addBehavior(
166170
new SoftDelete(
167171
[
@@ -255,7 +259,13 @@ public function afterCreate()
255259
/**
256260
* Queue notifications to be sent
257261
*/
258-
$this->getDI()->getShared('queue')->put($toNotify);
262+
/** @var Beanstalk $queue */
263+
$queue = $this->getDI()->getShared('queue');
264+
$queue->choose('notifications');
265+
/** @var DiscordComponent $discord */
266+
$discord = $this->getDI()->getShared('discord');
267+
$queue->put($toNotify);
268+
$discord->addMessageAboutDiscussion($this);
259269
}
260270
}
261271

app/model/PostsReplies.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717

1818
namespace Phosphorum\Model;
1919

20+
use DateTime;
21+
use DateTimeZone;
2022
use Phalcon\Diff;
2123
use Phalcon\Mvc\Model;
2224
use Phalcon\Diff\Renderer\Html\SideBySide;
2325
use Phalcon\Mvc\Model\Behavior\Timestampable;
26+
use Phalcon\Queue\Beanstalk;
27+
use Phosphorum\Discord\DiscordComponent;
2428

2529
/**
2630
* Class PostsReplies
@@ -92,6 +96,8 @@ public function initialize()
9296
]
9397
);
9498

99+
$this->keepSnapshots(true);
100+
95101
$this->addBehavior(
96102
new Timestampable([
97103
'beforeCreate' => [
@@ -228,7 +234,13 @@ public function afterCreate()
228234
/**
229235
* Queue notifications to be sent
230236
*/
231-
$this->getDI()->getShared('queue')->put($toNotify);
237+
/** @var Beanstalk $queue */
238+
$queue = $this->getDI()->getShared('queue');
239+
$queue->choose('notifications');
240+
$queue->put($toNotify);
241+
/** @var DiscordComponent $discord */
242+
$discord = $this->getDI()->getShared('discord');
243+
$discord->addMessageAboutReply($this);
232244
}
233245
}
234246

@@ -243,6 +255,11 @@ public function afterSave()
243255
$history->content = $this->content;
244256

245257
$history->save();
258+
if ($this->hasUpdated('accepted') && $this->accepted == 'Y') {
259+
/** @var DiscordComponent $discord */
260+
$discord = $this->getDI()->getShared('discord');
261+
$discord->addMessageAboutSolvedDiscussion($this);
262+
}
246263
}
247264

248265
public function afterDelete()
@@ -326,4 +343,16 @@ public function getDifference()
326343

327344
return $difference;
328345
}
346+
347+
/**
348+
* Returns a W3C date to be used in the sitemap.
349+
*
350+
* @return string
351+
*/
352+
public function getUTCCreatedAt()
353+
{
354+
$modifiedAt = new DateTime('@' . $this->created_at, new DateTimeZone('UTC'));
355+
356+
return $modifiedAt->format('Y-m-d\TH:i:s\Z');
357+
}
329358
}

app/provider/Config/ServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class ServiceProvider extends AbstractServiceProvider
4949
'email',
5050
'mailer',
5151
'config',
52+
'discord'
5253
];
5354

5455
/**

0 commit comments

Comments
 (0)