Skip to content

Commit 4912769

Browse files
authored
(Feature preview) Searching guild messages (#1389)
1 parent 7959e20 commit 4912769

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

src/Discord/Parts/Guild/Guild.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
use Discord\Repository\Guild\ScheduledEventRepository;
4545
use Discord\Repository\Guild\GuildTemplateRepository;
4646
use Discord\Repository\Guild\IntegrationRepository;
47+
use Discord\Repository\Guild\MessageRepository;
4748
use React\Promise\PromiseInterface;
4849
use ReflectionClass;
4950
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -154,6 +155,7 @@
154155
* @property CommandPermissionsRepository $command_permissions
155156
* @property IntegrationRepository $integrations
156157
* @property InviteRepository $invites
158+
* @property MessageRepository $messages
157159
* @property SoundRepository $sounds
158160
* @property GuildTemplateRepository $templates
159161
*/
@@ -315,6 +317,7 @@ class Guild extends Part
315317
'command_permissions' => CommandPermissionsRepository::class,
316318
'integrations' => IntegrationRepository::class,
317319
'invites' => InviteRepository::class,
320+
'messages' => MessageRepository::class,
318321
'sounds' => SoundRepository::class,
319322
'templates' => GuildTemplateRepository::class,
320323
];
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is a part of the DiscordPHP project.
7+
*
8+
* Copyright (c) 2015-present David Cole <[email protected]>
9+
*
10+
* This file is subject to the MIT license that is bundled
11+
* with this source code in the LICENSE.md file.
12+
*/
13+
14+
namespace Discord\Parts\Guild;
15+
16+
use Discord\Helpers\Collection;
17+
use Discord\Helpers\ExCollectionInterface;
18+
use Discord\Parts\Channel\Message;
19+
use Discord\Parts\Part;
20+
use Discord\Parts\Thread\Thread;
21+
use Discord\Parts\User\Member;
22+
23+
/**
24+
* TODO.
25+
*
26+
* @link TODO
27+
*
28+
* @property string $analytics_id
29+
* @property ExCollectionInterface<Message> $messages
30+
* @property bool $doing_deep_historical_index
31+
* @property int $total_results
32+
* @property ExCollectionInterface<Thread>|Thread[] $threads
33+
* @property ExCollectionInterface<Member>|Member[] $members
34+
* @property ?int|null $documents_indexed
35+
*/
36+
class GuildSearch extends Part
37+
{
38+
/**
39+
* @inheritDoc
40+
*/
41+
protected $fillable = [
42+
'analytics_id',
43+
'messages',
44+
'doing_deep_historical_index',
45+
'total_results',
46+
'threads',
47+
'members',
48+
'documents_indexed',
49+
];
50+
51+
/**
52+
* Returns a collection of messages found in the search.
53+
*
54+
* @return ExCollectionInterface|Message[]|null
55+
*/
56+
protected function getMessagesAttribute(): ?ExCollectionInterface
57+
{
58+
if (! isset($this->attributes['messages'])) {
59+
return null;
60+
}
61+
62+
$collection = Collection::for(Message::class);
63+
64+
foreach ($this->attributes['messages'] as $snowflake => $message) {
65+
if ($guild = $this->discord->guilds->get('id', $this->guild_id)) {
66+
if ($channel = $guild->channels->get('id', $message->channel_id)) {
67+
$messagePart = $channel->messages->get('id', $snowflake);
68+
}
69+
}
70+
71+
$collection->pushItem($messagePart ?? $this->factory->part(Message::class, (array) $message + ['guild_id' => $this->guild_id], true));
72+
}
73+
74+
return $collection;
75+
}
76+
77+
/**
78+
* Returns a collection of members found in the search.
79+
*
80+
* @return ExCollectionInterface|Member[]|null
81+
*/
82+
protected function getMembersAttribute(): ?ExCollectionInterface
83+
{
84+
$collection = Collection::for(Member::class);
85+
86+
foreach ($this->attributes['members'] ?? [] as $snowflake => $member) {
87+
if ($guild_id = $member->guild_id ?? null) {
88+
if ($guild = $this->discord->guilds->get('id', $guild_id)) {
89+
$memberPart = $guild->members->get('id', $snowflake);
90+
}
91+
}
92+
93+
if (! isset($memberPart)) {
94+
$member->user = $this->attributes['users']->$snowflake;
95+
$memberPart = $this->factory->part(Member::class, (array) $member + ['guild_id' => $this->guild_id], true);
96+
}
97+
98+
$collection->pushItem($memberPart);
99+
}
100+
101+
return $collection;
102+
}
103+
104+
/**
105+
* Returns a collection of threads found in the search.
106+
*
107+
* @return ExCollectionInterface|Thread[]
108+
*/
109+
protected function getThreadsAttribute(): ExCollectionInterface
110+
{
111+
$collection = Collection::for(Thread::class);
112+
113+
foreach ($this->attributes['threads'] ?? [] as $thread) {
114+
$collection->pushItem($this->factory->part(Thread::class, (array) $thread, true));
115+
}
116+
117+
return $collection;
118+
}
119+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is a part of the DiscordPHP project.
7+
*
8+
* Copyright (c) 2015-present David Cole <[email protected]>
9+
*
10+
* This file is subject to the MIT license that is bundled
11+
* with this source code in the LICENSE.md file.
12+
*/
13+
14+
namespace Discord\Repository\Guild;
15+
16+
use Discord\Http\Endpoint;
17+
use Discord\Parts\Channel\Message;
18+
use Discord\Parts\Guild\GuildSearch;
19+
use Discord\Repository\AbstractRepository;
20+
use React\Promise\PromiseInterface;
21+
22+
use function React\Promise\resolve;
23+
24+
/**
25+
* Used only to search messages sent in a guild.
26+
*
27+
* All bots with the message content intent can use it, but it's not considered stable yet, and Discord might make changes or remove bot access if necessary.
28+
*
29+
* @see \Discord\Parts\Guild\GuildSearch
30+
*
31+
* @since 10.19.0
32+
*
33+
* @method Message|null get(string $discrim, $key)
34+
* @method Message|null pull(string|int $key, $default = null)
35+
* @method Message|null first()
36+
* @method Message|null last()
37+
* @method Message|null find(callable $callback)
38+
*
39+
* @return PromiseInterface<static>
40+
* @throws \Exception
41+
*/
42+
class MessageRepository extends AbstractRepository
43+
{
44+
/**
45+
* The collection discriminator.
46+
*
47+
* @var string Discriminator.
48+
*/
49+
protected $discrim = 'analytics_id';
50+
51+
/**
52+
* @inheritDoc
53+
*/
54+
protected $endpoints = [
55+
'all' => Endpoint::GUILD_MESSAGES_SEARCH,
56+
];
57+
58+
/**
59+
* @inheritDoc
60+
*/
61+
protected $class = GuildSearch::class;
62+
63+
/**
64+
* Freshens the repository cache.
65+
*
66+
* @param array $queryparams Query string params to add to the request (no validation).
67+
* Supported parameters:
68+
* - sort_by: Sorting mode. See SortingMode schema.
69+
* - sort_order: Sorting order. See SortingOrder schema.
70+
* - content: Message content to search for (string, max 1024 chars).
71+
* - slop: Integer, minimum 0, maximum 100.
72+
* - contents: Array of message contents to search for (string|null, max 1024 chars each, up to 100 items).
73+
* - author_id: Author ID (SnowflakeType|null, up to 1521 unique items).
74+
* - author_type: Author type (AuthorType, up to 1521 unique items).
75+
* - mentions: Mentioned user ID (SnowflakeType|null, up to 1521 unique items).
76+
* - mention_everyone: Boolean, whether to include messages mentioning everyone.
77+
* - min_id: Minimum message ID (SnowflakeType).
78+
* - max_id: Maximum message ID (SnowflakeType).
79+
* - limit: Integer, minimum 1, maximum 25.
80+
* - offset: Integer, minimum 0, maximum 9975.
81+
* - cursor: Cursor for pagination (ScoreCursor or TimestampCursor).
82+
* - has: Message feature (HasOption, up to 1521 unique items).
83+
* - link_hostname: Link hostname (string|null, max 152133 chars each, up to 1521 unique items).
84+
* - embed_provider: Embed providers (string|null, max 256 chars each, up to 1521 unique items).
85+
* - embed_type: Embed type (SearchableEmbedType|null, up to 1521 unique items).
86+
* - attachment_extension: Attachment extension (string|null, max 152133 chars each, up to 1521 unique items).
87+
* - attachment_filename: Attachment filename (string, max 1024 chars).
88+
* - pinned: Boolean, whether to include pinned messages.
89+
* - command_id: Command ID (SnowflakeType).
90+
* - command_name: Command name (string, max 32 chars).
91+
* - include_nsfw: Boolean, whether to include NSFW messages.
92+
* - channel_id: Channel IDs (SnowflakeType, up to 500 unique items).
93+
*
94+
* @return PromiseInterface<static>
95+
*
96+
* @throws \Exception
97+
*/
98+
public function freshen(array $queryparams = []): PromiseInterface
99+
{
100+
if (empty($queryparams)) {
101+
return resolve($this);
102+
}
103+
104+
$endpoint = new Endpoint($this->endpoints['all']);
105+
$endpoint->bindAssoc($this->vars);
106+
107+
foreach ($queryparams as $query => $param) {
108+
$endpoint->addQuery($query, $param);
109+
}
110+
111+
return $this->http->get($endpoint)->then(function ($response) {
112+
$part = $this->factory->create($this->class, (array) $response, true);
113+
return $this->cacheFreshen($part);
114+
});
115+
}
116+
117+
/**
118+
* @param object $response
119+
*
120+
* @return PromiseInterface<static>
121+
*/
122+
protected function cacheFreshen($response): PromiseInterface
123+
{
124+
return $this->cache->set($response->{$this->discrim}, (array) $response)->then(fn ($success) => $this);
125+
}
126+
}

0 commit comments

Comments
 (0)