소스 검색

IRCClient: Add handling of some basic messages and commands.

Andreas Kling 6 년 전
부모
커밋
f44ba6a4c6

+ 21 - 0
Applications/IRCClient/IRCChannel.cpp

@@ -55,3 +55,24 @@ void IRCChannel::say(const String& text)
     m_client.send_privmsg(m_name, text);
     add_message(' ', m_client.nickname(), text);
 }
+
+void IRCChannel::handle_join(const String& nick, const String& hostmask)
+{
+    if (nick == m_client.nickname())
+        m_open = true;
+    add_message(' ', "", String::format("*** %s [%s] has joined %s", nick.characters(), hostmask.characters(), m_name.characters()));
+}
+
+void IRCChannel::handle_part(const String& nick, const String& hostmask)
+{
+    if (nick == m_client.nickname())
+        m_open = false;
+    add_message(' ', "", String::format("*** %s [%s] has parted from %s", nick.characters(), hostmask.characters(), m_name.characters()));
+}
+
+void IRCChannel::handle_topic(const String& nick, const String& topic)
+{
+    if (nick == m_client.nickname())
+        m_open = false;
+    add_message(' ', "", String::format("*** %s set topic to \"%s\"", nick.characters(), topic.characters()));
+}

+ 7 - 0
Applications/IRCClient/IRCChannel.h

@@ -39,14 +39,21 @@ public:
     int member_count() const { return m_members.size(); }
     String member_at(int i) { return m_members[i].name; }
 
+    void handle_join(const String& nick, const String& hostmask);
+    void handle_part(const String& nick, const String& hostmask);
+    void handle_topic(const String& nick, const String& topic);
+
     IRCWindow& window() { return *m_window; }
     const IRCWindow& window() const { return *m_window; }
 
+    String topic() const { return m_topic; }
+
 private:
     IRCChannel(IRCClient&, const String&);
 
     IRCClient& m_client;
     String m_name;
+    String m_topic;
     struct Member {
         String name;
         char prefix { 0 };

+ 86 - 2
Applications/IRCClient/IRCClient.cpp

@@ -11,9 +11,11 @@
 #include <unistd.h>
 #include <stdio.h>
 
-//#define IRC_DEBUG
+#define IRC_DEBUG
 
 enum IRCNumeric {
+    RPL_TOPIC = 332,
+    RPL_TOPICWHOTIME = 333,
     RPL_NAMREPLY = 353,
     RPL_ENDOFNAMES = 366,
 };
@@ -201,6 +203,11 @@ void IRCClient::join_channel(const String& channel_name)
     send(String::format("JOIN %s\r\n", channel_name.characters()));
 }
 
+void IRCClient::part_channel(const String& channel_name)
+{
+    send(String::format("PART %s\r\n", channel_name.characters()));
+}
+
 void IRCClient::handle(const Message& msg, const String&)
 {
 #ifdef IRC_DEBUG
@@ -225,6 +232,9 @@ void IRCClient::handle(const Message& msg, const String&)
         case RPL_NAMREPLY:
             handle_namreply(msg);
             return;
+        case RPL_TOPIC:
+            handle_rpl_topic(msg);
+            return;
         }
     }
 
@@ -234,6 +244,12 @@ void IRCClient::handle(const Message& msg, const String&)
     if (msg.command == "JOIN")
         return handle_join(msg);
 
+    if (msg.command == "PART")
+        return handle_part(msg);
+
+    if (msg.command == "TOPIC")
+        return handle_topic(msg);
+
     if (msg.command == "PRIVMSG")
         return handle_privmsg(msg);
 
@@ -252,6 +268,8 @@ void IRCClient::handle_user_input_in_channel(const String& channel_name, const S
 {
     if (input.is_empty())
         return;
+    if (input[0] == '/')
+        return handle_user_command(input);
     ensure_channel(channel_name).say(input);
 }
 
@@ -259,6 +277,8 @@ void IRCClient::handle_user_input_in_query(const String& query_name, const Strin
 {
     if (input.is_empty())
         return;
+    if (input[0] == '/')
+        return handle_user_command(input);
     ensure_query(query_name).say(input);
 }
 
@@ -266,6 +286,8 @@ void IRCClient::handle_user_input_in_server(const String& input)
 {
     if (input.is_empty())
         return;
+    if (input[0] == '/')
+        return handle_user_command(input);
 }
 
 bool IRCClient::is_nick_prefix(char ch) const
@@ -349,8 +371,47 @@ void IRCClient::handle_join(const Message& msg)
 {
     if (msg.arguments.size() != 1)
         return;
+    auto prefix_parts = msg.prefix.split('!');
+    if (prefix_parts.size() < 1)
+        return;
+    auto nick = prefix_parts[0];
+    auto& channel_name = msg.arguments[0];
+    ensure_channel(channel_name).handle_join(nick, msg.prefix);
+}
+
+void IRCClient::handle_part(const Message& msg)
+{
+    if (msg.arguments.size() != 1)
+        return;
+    auto prefix_parts = msg.prefix.split('!');
+    if (prefix_parts.size() < 1)
+        return;
+    auto nick = prefix_parts[0];
+    auto& channel_name = msg.arguments[0];
+    ensure_channel(channel_name).handle_part(nick, msg.prefix);
+}
+
+void IRCClient::handle_topic(const Message& msg)
+{
+    if (msg.arguments.size() != 2)
+        return;
+    auto prefix_parts = msg.prefix.split('!');
+    if (prefix_parts.size() < 1)
+        return;
+    auto nick = prefix_parts[0];
     auto& channel_name = msg.arguments[0];
-    ensure_channel(channel_name);
+    ensure_channel(channel_name).handle_topic(nick, msg.arguments[1]);
+}
+
+void IRCClient::handle_rpl_topic(const Message& msg)
+{
+    if (msg.arguments.size() != 3)
+        return;
+    auto& nick = msg.arguments[0];
+    auto& channel_name = msg.arguments[1];
+    auto& topic = msg.arguments[2];
+    ensure_channel(channel_name).handle_topic(nick, topic);
+    // FIXME: Handle RPL_TOPICWHOTIME so we can know who set it and when.
 }
 
 void IRCClient::handle_namreply(const Message& msg)
@@ -403,3 +464,26 @@ void IRCClient::unregister_subwindow(IRCWindow& subwindow)
     }
     m_client_window_list_model->update();
 }
+
+void IRCClient::handle_user_command(const String& input)
+{
+    auto parts = input.split(' ');
+    if (parts.is_empty())
+        return;
+    auto command = parts[0].to_uppercase();
+    if (command == "/JOIN") {
+        if (parts.size() >= 2)
+            join_channel(parts[1]);
+        return;
+    }
+    if (command == "/PART") {
+        if (parts.size() >= 2)
+            part_channel(parts[1]);
+        return;
+    }
+    if (command == "/QUERY") {
+        if (parts.size() >= 2)
+            ensure_query(parts[1]);
+        return;
+    }
+}

+ 5 - 0
Applications/IRCClient/IRCClient.h

@@ -27,6 +27,7 @@ public:
     String nickname() const { return m_nickname; }
 
     void join_channel(const String&);
+    void part_channel(const String&);
 
     bool is_nick_prefix(char) const;
 
@@ -73,10 +74,14 @@ private:
     void send_privmsg(const String& target, const String&);
     void process_line();
     void handle_join(const Message&);
+    void handle_part(const Message&);
     void handle_ping(const Message&);
+    void handle_topic(const Message&);
+    void handle_rpl_topic(const Message&);
     void handle_namreply(const Message&);
     void handle_privmsg(const Message&);
     void handle(const Message&, const String& verbatim);
+    void handle_user_command(const String&);
 
     String m_hostname;
     int m_port { 0 };

+ 4 - 1
Applications/IRCClient/IRCLogBufferModel.cpp

@@ -51,7 +51,10 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role) const
         auto* tm = localtime(&entry.timestamp);
         return String::format("%02u:%02u:%02u", tm->tm_hour, tm->tm_min, tm->tm_sec);
     }
-    case Column::Name: return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters());
+    case Column::Name:
+        if (entry.sender.is_empty())
+            return String::empty();
+        return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters());
     case Column::Text: return entry.text;
     }
     ASSERT_NOT_REACHED();