Passed
Pull Request — master (#12)
by
unknown
08:46
created

IrcClient::say()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jerodev\PhpIrcClient;
6
7
use Exception;
8
use Jerodev\PhpIrcClient\Helpers\EventHandlerCollection;
9
use Jerodev\PhpIrcClient\Messages\IrcMessage;
10
use Jerodev\PhpIrcClient\Options\ClientOptions;
11
12
class IrcClient
13
{
14
    /** @var array<string, IrcChannel> */
15
    private array $channels = [];
16
    private IrcConnection $connection;
17
    private bool $isAuthenticated = false;
18
    private EventHandlerCollection $messageEventHandlers;
19
    private ClientOptions $options;
20
    private ?IrcUser $user = null;
21
22
    /**
23
     * Create a new IrcClient instance.
24
     *
25
     * @param string $server The server address to connect to including the port: `address:port`.
26
     * @param ClientOptions $options An object depicting options for this connection.
27
     */
28
    public function __construct(string $server, ?ClientOptions $options = null)
29
    {
30
        $this->options = $options ?? new ClientOptions();
31
        $this->connection = new IrcConnection($server, $this->options->connectionOptions());
32
33
        if (null !== $this->options->nickname) {
34
            $this->user = new IrcUser($this->options->nickname);
35
        }
36
        $this->messageEventHandlers = new EventHandlerCollection();
37
38
        if (!empty($this->options->channels)) {
39
            foreach ($this->options->channels as $channel) {
40
                $this->channels[$channel] = new IrcChannel($channel);
41
            }
42
        }
43
44
        if ($this->options->autoConnect) {
45
            $this->connect();
46
        }
47
    }
48
49
    /**
50
     * Set the user credentials for the connections.
51
     *
52
     * When a connection is already open, this function can be used to change
53
     * the nickname of the client.
54
     * @param IrcUser|string $user The user information.
55
     */
56
    public function setUser(IrcUser | string $user): void
57
    {
58
        if (is_string($user)) {
59
            $user = new IrcUser($user);
60
        }
61
62
        if ($this->connection->isConnected() && $this->user->nickname !== $user->nickname) {
63
            $this->send("NICK :$user->nickname");
64
        }
65
66
        $this->user = $user;
67
    }
68
69
    /**
70
     * Connect to the irc server and start listening for messages.
71
     * @throws Exception if no user information is provided before connecting.
72
     */
73
    public function connect(): void
74
    {
75
        if (!$this->user) {
76
            throw new Exception(
77
                'A nickname must be set before connecting to an irc server.'
78
            );
79
        }
80
81
        if ($this->connection->isConnected()) {
82
            return;
83
        }
84
85
        $this->isAuthenticated = false;
86
        $this->connection->onData(function (IrcMessage $msg): void {
87
            $this->handleIrcMessage($msg);
88
        });
89
        $this->connection->open();
90
    }
91
92
    /**
93
     * Close the current connection, if any.
94
     */
95
    public function disconnect(): void
96
    {
97
        $this->connection->close();
98
    }
99
100
    /**
101
     * Register to an event callback.
102
     * @param string $event The event to register to.
103
     * @param callable $callback The callback to be execute when the event is emitted.
104
     */
105
    public function on(string $event, callable $callback): void
106
    {
107
        $this->messageEventHandlers->addHandler($event, $callback);
108
    }
109
110
    /**
111
     * Send a raw command string to the IRC server.
112
     * @param string $command The full command string to send.
113
     */
114
    public function send(string $command): void
115
    {
116
        $this->connection->write($command);
117
    }
118
119
    /**
120
     * Send a message to a channel or user.
121
     * To send to a channel, make sure the `$target` starts with a `#`.
122
     * @param string $target The channel or user to message.
123
     * @param string $message The message to send.
124
     */
125
    public function say(string $target, string $message): void
126
    {
127
        foreach (explode("\n", $message) as $msg) {
128
            $this->send("PRIVMSG $target :" . trim($msg));
129
        }
130
    }
131
132
    /**
133
     * Join an IRC channel.
134
     * @param string $channel The name of the channel to join.
135
     */
136
    public function join(string $channel): void
137
    {
138
        $channel = $this->channelName($channel);
139
        $this->send("JOIN $channel");
140
        $this->getChannel($channel);
141
    }
142
143
    /**
144
     * Part from an IRC channel.
145
     * @param string $channel The name of the channel to leave.
146
     */
147
    public function part(string $channel): void
148
    {
149
        $channel = $this->channelName($channel);
150
151
        if (array_key_exists($channel, $this->channels)) {
152
            $this->send("PART $channel");
153
        }
154
    }
155
156
    /**
157
     * Grab channel information by its name.
158
     * This function makes sure the channel exists on this client first.
159
     */
160
    public function getChannel(string $channel): IrcChannel
161
    {
162
        $channel = $this->channelName($channel);
163
164
        if (($this->channels[$channel] ?? null) === null) {
165
            $this->channels[$channel] = new IrcChannel($channel);
166
        }
167
168
        return $this->channels[$channel];
169
    }
170
171
    /**
172
     * Get the name with which the client is currently known on the server.
173
     */
174
    public function getNickname(): ?string
175
    {
176
        if (null === $this->user) {
177
            return null;
178
        }
179
        return $this->user->nickname;
180
    }
181
182
    /**
183
     * Return a list of all channels.
184
     * @return array<string, IrcChannel>
185
     */
186
    public function getChannels(): array
187
    {
188
        return $this->channels;
189
    }
190
191
    /**
192
     * Indicates whether the client should autorejoin channels when kicked.
193
     */
194
    public function shouldAutoRejoin(): bool
195
    {
196
        return $this->options->autoRejoin;
197
    }
198
199
    /**
200
     * Take actions required for received IRC messages and invoke the correct
201
     * event handlers.
202
     * @param IrcMessage $message The message object for the received line.
203
     */
204
    private function handleIrcMessage(IrcMessage $message): void
205
    {
206
        $message->injectChannel($this->channels);
207
        $message->handle($this);
208
209
        if (!$this->isAuthenticated && null !== $this->user) {
210
            $this->send("USER {$this->user->nickname} * * :{$this->user->nickname}");
211
            $this->send("NICK {$this->user->nickname}");
212
            $this->isAuthenticated = true;
213
        }
214
215
        foreach ($message->getEvents() as $event) {
216
            $this->messageEventHandlers->invoke($event);
217
        }
218
    }
219
220
    /**
221
     * Make sure all channel names have the same format.
222
     */
223
    private function channelName(string $channel): string
224
    {
225
        if ($channel[0] !== '#') {
226
            $channel = "#$channel";
227
        }
228
229
        return $channel;
230
    }
231
232
    public function getConnection(): IrcConnection
233
    {
234
        return $this->connection;
235
    }
236
}
237