Basic command storage and parsing
A new class was added (ChatCommand) to represent commands, which are all stored in the Server iself. A command can be assigned any arbitrary BMessage, and can be given arguments with some semantic meaning (so far just "string" or "room participant," etc). "invite" and moderation commands were added (ban/kick/mute etc).
This commit is contained in:
parent
a4e2b1dc5a
commit
46d6d0a0b0
|
@ -54,7 +54,9 @@ enum im_what_code {
|
|||
IM_SEND_MESSAGE = 20,
|
||||
|
||||
//! Chat message has been sent →Caya
|
||||
// Requires: String "chat_id", String "user_id", String "body"
|
||||
// If no user_id is specified, it's treated as a system message
|
||||
// Requires: String "chat_id", String "body"
|
||||
// Allows: String "user_id"
|
||||
IM_MESSAGE_SENT = 21,
|
||||
|
||||
//! Chat message received →Caya
|
||||
|
@ -76,7 +78,6 @@ enum im_what_code {
|
|||
IM_USER_STOPPED_TYPING = 25,
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Messages related to contact changes.
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <FindDirectory.h>
|
||||
#include <IconUtils.h>
|
||||
#include <Path.h>
|
||||
#include <StringList.h>
|
||||
|
||||
#include <kernel/fs_attr.h>
|
||||
|
||||
|
@ -40,6 +41,31 @@ CayaStatusToString(CayaStatus status)
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
IsCommand(BString line)
|
||||
{
|
||||
return line.StartsWith("/");
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
CommandName(BString line)
|
||||
{
|
||||
BStringList words;
|
||||
line.Split(" ", true, words);
|
||||
return words.StringAt(0).RemoveFirst("/");
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
CommandArgs(BString line)
|
||||
{
|
||||
BString remove("/");
|
||||
remove << CommandName(line) << " ";
|
||||
return line.RemoveFirst(remove);
|
||||
}
|
||||
|
||||
|
||||
BResources*
|
||||
CayaResources()
|
||||
{
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
const char* CayaStatusToString(CayaStatus status);
|
||||
|
||||
bool IsCommand(BString line);
|
||||
BString CommandName(BString line);
|
||||
BString CommandArgs(BString line);
|
||||
|
||||
BResources* CayaResources();
|
||||
|
||||
const char* CayaAccountsPath();
|
||||
|
@ -33,7 +37,7 @@ const char* CayaContactCachePath(const char* accountName, const char* userIdenti
|
|||
rgb_color CayaTintColor(rgb_color color, int severity);
|
||||
rgb_color CayaForegroundColor(rgb_color background);
|
||||
|
||||
// Borrowed from BePodder's own libfunky
|
||||
// Borrowed from BePodder's own libfunky. Groovy B)
|
||||
status_t ReadAttributeData(BNode* node, const char* name, char** buffer, int32 *size);
|
||||
status_t WriteAttributeMessage(BNode* node, const char* name, BMessage* data);
|
||||
status_t ReadAttributeMessage(BNode* node, const char* name, BMessage* data);
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include "ChatCommand.h"
|
||||
|
||||
#include <StringList.h>
|
||||
|
||||
#include "Conversation.h"
|
||||
#include "MainWindow.h"
|
||||
#include "TheApp.h"
|
||||
|
||||
|
||||
ChatCommand::ChatCommand(const char* name, BMessage msg, bool toProtocol,
|
||||
List<int32> argTypes)
|
||||
:
|
||||
fName(name),
|
||||
fMessage(msg),
|
||||
fToProto(toProtocol),
|
||||
fArgTypes(argTypes)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ChatCommand::SetDesc(const char* description)
|
||||
{
|
||||
fDescription = description;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ChatCommand::Parse(BString args, BString* errorMsg, Conversation* chat)
|
||||
{
|
||||
BMessage* msg = new BMessage(fMessage);
|
||||
msg->AddString("chat_id", chat->GetId());
|
||||
msg->AddInt64("instance", chat->GetProtocolLooper()->GetInstance());
|
||||
|
||||
if (fArgTypes.CountItems() == 0) {
|
||||
msg->AddString("misc_str", args);
|
||||
return _Send(msg, chat);
|
||||
}
|
||||
|
||||
if (_ProcessArgs(args, msg, errorMsg, chat) == true)
|
||||
return _Send(msg, chat);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ChatCommand::_ProcessArgs(BString args, BMessage* msg, BString* errorMsg,
|
||||
Conversation* chat)
|
||||
{
|
||||
int32 argCount = fArgTypes.CountItems();
|
||||
BStringList argList;
|
||||
args.Split(" ", false, argList);
|
||||
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
BString arg = argList.StringAt(i);
|
||||
const char* strName = "misc_str";
|
||||
|
||||
switch (fArgTypes.ItemAt(i))
|
||||
{
|
||||
case CMD_ROOM_PARTICIPANT:
|
||||
{
|
||||
if (chat->UserById(arg) == NULL) {
|
||||
errorMsg->SetTo("%user% isn't a member of this room.");
|
||||
errorMsg->ReplaceAll("%user%", arg);
|
||||
return false;
|
||||
}
|
||||
msg->AddString("user_id", arg);
|
||||
break;
|
||||
}
|
||||
case CMD_KNOWN_USER:
|
||||
{
|
||||
if (chat->GetProtocolLooper()->UserById(arg) == NULL) {
|
||||
errorMsg->SetTo("You aren't contacts with and have no chats "
|
||||
"in common with %user%. Shame.");
|
||||
errorMsg->ReplaceAll("%user%", arg);
|
||||
return false;
|
||||
}
|
||||
msg->AddString("user_id", arg);
|
||||
break;
|
||||
}
|
||||
case CMD_ANY_USER:
|
||||
msg->AddString("user_id", arg);
|
||||
break;
|
||||
case CMD_BODY_STRING:
|
||||
strName = "body";
|
||||
default:
|
||||
// If string's the last argument, it can be longer than one word
|
||||
if (i == (argCount - 1) && argList.CountStrings() > argCount)
|
||||
for (int j = i + 1; j < argList.CountStrings(); j++)
|
||||
arg << " " << argList.StringAt(j);
|
||||
msg->AddString(strName, arg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ChatCommand::_Send(BMessage* msg, Conversation* chat)
|
||||
{
|
||||
if (fToProto == true)
|
||||
chat->GetProtocolLooper()->PostMessage(msg);
|
||||
else
|
||||
((TheApp*)be_app)->GetMainWindow()->PostMessage(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef CHAT_COMMAND_H
|
||||
#define CHAT_COMMAND_H
|
||||
|
||||
#include <Message.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <libsupport/KeyMap.h>
|
||||
|
||||
class Conversation;
|
||||
|
||||
|
||||
enum cmd_arg_type
|
||||
{
|
||||
CMD_ROOM_PARTICIPANT = 'CArp', // "user_id" in message
|
||||
CMD_KNOWN_USER = 'CAku', // "user_id" in message
|
||||
CMD_ANY_USER = 'CAau', // "user_id" in message
|
||||
CMD_BODY_STRING = 'CAbs', // "body" in message
|
||||
CMD_MISC_STRING = 'CAms' // "misc_str" in message
|
||||
};
|
||||
|
||||
|
||||
class ChatCommand {
|
||||
public:
|
||||
ChatCommand(const char* name, BMessage msg, bool toProtocol,
|
||||
List<int32> argTypes);
|
||||
|
||||
const char* GetName() { return fName.String(); }
|
||||
|
||||
void SetDesc(const char* description);
|
||||
const char* GetDesc() { return fDescription.String(); }
|
||||
|
||||
bool Parse(BString args, BString* errorMsg, Conversation* chat);
|
||||
|
||||
private:
|
||||
bool _ProcessArgs(BString args, BMessage* msg, BString* errorMsg,
|
||||
Conversation* chat);
|
||||
|
||||
bool _Send(BMessage* msg, Conversation* chat);
|
||||
|
||||
BString fName;
|
||||
BString fDescription;
|
||||
BMessage fMessage;
|
||||
|
||||
bool fToProto;
|
||||
List<int32> fArgTypes;
|
||||
};
|
||||
|
||||
|
||||
typedef KeyMap<BString, ChatCommand*> CommandMap;
|
||||
|
||||
|
||||
#endif // CHAT_COMMAND_H
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include "CayaProtocolMessages.h"
|
||||
#include "CayaRenderView.h"
|
||||
#include "CayaUtils.h"
|
||||
#include "ChatCommand.h"
|
||||
#include "ConversationItem.h"
|
||||
#include "ConversationView.h"
|
||||
#include "MainWindow.h"
|
||||
|
@ -82,7 +83,27 @@ Conversation::ImMessage(BMessage* msg)
|
|||
}
|
||||
case IM_SEND_MESSAGE:
|
||||
{
|
||||
fMessenger.SendMessage(msg);
|
||||
BString body;
|
||||
if (msg->FindString("body", &body) != B_OK)
|
||||
break;
|
||||
|
||||
if (IsCommand(body.String()) == false) {
|
||||
fMessenger.SendMessage(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
BString name = CommandName(body);
|
||||
BString args = CommandArgs(body);
|
||||
ChatCommand* cmd = _GetServer()->CommandById(name);
|
||||
|
||||
if (cmd == NULL) {
|
||||
_WarnUser(BString("That isn't a valid command."));
|
||||
break;
|
||||
}
|
||||
|
||||
BString error("");
|
||||
if (cmd->Parse(args, &error, this) == false)
|
||||
_WarnUser(error);
|
||||
break;
|
||||
}
|
||||
case IM_ROOM_METADATA:
|
||||
|
@ -330,6 +351,16 @@ Conversation::GetRole(BString id)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
Conversation::_WarnUser(BString message)
|
||||
{
|
||||
BMessage* warning = new BMessage(IM_MESSAGE);
|
||||
warning->AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
||||
warning->AddString("body", message.Append('\n', 1).InsertChars("-- ", 0));
|
||||
GetView()->MessageReceived(warning);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Conversation::_LogChatMessage(BMessage* msg)
|
||||
{
|
||||
|
@ -449,8 +480,7 @@ Conversation::_EnsureUser(BMessage* msg)
|
|||
}
|
||||
// Not anywhere; create user
|
||||
else if (user == NULL) {
|
||||
user = new User(id,
|
||||
((TheApp*)be_app)->GetMainWindow()->GetServer()->Looper());
|
||||
user = new User(id, _GetServer()->Looper());
|
||||
user->SetProtocolLooper(fLooper);
|
||||
|
||||
fLooper->AddUser(user);
|
||||
|
@ -466,3 +496,10 @@ Conversation::_EnsureUser(BMessage* msg)
|
|||
}
|
||||
|
||||
|
||||
Server*
|
||||
Conversation::_GetServer()
|
||||
{
|
||||
return ((TheApp*)be_app)->GetMainWindow()->GetServer();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
int32 GetFlags(int32 flags) { return fRoomFlags; }
|
||||
|
||||
private:
|
||||
void _WarnUser(BString message);
|
||||
|
||||
void _LogChatMessage(BMessage* msg);
|
||||
status_t _GetChatLogs(BMessage* msg);
|
||||
|
||||
|
@ -78,9 +80,10 @@ private:
|
|||
void _LoadRoomFlags();
|
||||
|
||||
void _EnsureCachePath();
|
||||
|
||||
User* _EnsureUser(BMessage* msg);
|
||||
|
||||
Server* _GetServer();
|
||||
|
||||
BMessenger fMessenger;
|
||||
ProtocolLooper* fLooper;
|
||||
ConversationView* fChatView;
|
||||
|
|
|
@ -36,6 +36,7 @@ SRCS = \
|
|||
application/AccountManager.cpp \
|
||||
application/CayaProtocolAddOn.cpp \
|
||||
application/CayaUtils.cpp \
|
||||
application/ChatCommand.cpp \
|
||||
application/Contact.cpp \
|
||||
application/Conversation.cpp \
|
||||
application/EditingFilter.cpp \
|
||||
|
|
|
@ -39,6 +39,7 @@ Server::Server()
|
|||
:
|
||||
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
|
||||
{
|
||||
_InitDefaultCommands();
|
||||
}
|
||||
|
||||
|
||||
|
@ -718,6 +719,20 @@ Server::AddConversation(Conversation* chat, int64 instance)
|
|||
}
|
||||
|
||||
|
||||
CommandMap
|
||||
Server::Commands()
|
||||
{
|
||||
return fCommands;
|
||||
}
|
||||
|
||||
|
||||
ChatCommand*
|
||||
Server::CommandById(BString id)
|
||||
{
|
||||
return fCommands.ValueFor(id);
|
||||
}
|
||||
|
||||
|
||||
ProtocolLooper*
|
||||
Server::_LooperFromMessage(BMessage* message)
|
||||
{
|
||||
|
@ -833,4 +848,69 @@ Server::_GetRole(BMessage* msg)
|
|||
return new Role(title, perms, priority);
|
||||
}
|
||||
|
||||
void
|
||||
Server::_InitDefaultCommands()
|
||||
{
|
||||
List<int32> roomUser;
|
||||
roomUser.AddItem(CMD_ROOM_PARTICIPANT);
|
||||
List<int32> kickBody;
|
||||
kickBody.AddItem(CMD_ROOM_PARTICIPANT);
|
||||
kickBody.AddItem(CMD_BODY_STRING);
|
||||
List<int32> knownUser;
|
||||
knownUser.AddItem(CMD_KNOWN_USER);
|
||||
List<int32> anyUser;
|
||||
anyUser.AddItem(CMD_ANY_USER);
|
||||
|
||||
BMessage kickMsg(IM_MESSAGE);
|
||||
kickMsg.AddInt32("im_what", IM_ROOM_KICK_PARTICIPANT);
|
||||
ChatCommand* kick = new ChatCommand("kick", kickMsg, true, kickBody);
|
||||
kick->SetDesc("Force a user to temporarily leave the room, assuming your "
|
||||
"power level's high enough.");
|
||||
fCommands.AddItem("kick", kick);
|
||||
|
||||
BMessage banMsg(IM_MESSAGE);
|
||||
banMsg.AddInt32("im_what", IM_ROOM_BAN_PARTICIPANT);
|
||||
ChatCommand* ban = new ChatCommand("ban", banMsg, true, kickBody);
|
||||
ban->SetDesc("Kick a user out of the room and slam the door behind them― "
|
||||
"locking it while you're at it.");
|
||||
fCommands.AddItem("ban", ban);
|
||||
|
||||
BMessage unbanMsg(IM_MESSAGE);
|
||||
unbanMsg.AddInt32("im_what", IM_ROOM_UNBAN_PARTICIPANT);
|
||||
ChatCommand* unban = new ChatCommand("unban", unbanMsg, true, anyUser);
|
||||
unban->SetDesc("Undo a previous ban, allowing the user to rejoin (if they "
|
||||
"still want to).");
|
||||
fCommands.AddItem("unban", unban);
|
||||
|
||||
BMessage muteMsg(IM_MESSAGE);
|
||||
muteMsg.AddInt32("im_what", IM_ROOM_MUTE_PARTICIPANT);
|
||||
ChatCommand* mute = new ChatCommand("mute", muteMsg, true, roomUser);
|
||||
mute->SetDesc("Disallow a user from sending visible messages.");
|
||||
fCommands.AddItem("mute", mute);
|
||||
|
||||
BMessage unmuteMsg(IM_MESSAGE);
|
||||
unmuteMsg.AddInt32("im_what", IM_ROOM_UNMUTE_PARTICIPANT);
|
||||
ChatCommand* unmute = new ChatCommand("unmute", unmuteMsg, true, roomUser);
|
||||
unmute->SetDesc("Restore a user's ability to send messages.");
|
||||
fCommands.AddItem("unmute", unmute);
|
||||
|
||||
BMessage deafenMsg(IM_MESSAGE);
|
||||
deafenMsg.AddInt32("im_what", IM_ROOM_DEAFEN_PARTICIPANT);
|
||||
ChatCommand* deafen = new ChatCommand("deafen", deafenMsg, true, roomUser);
|
||||
deafen->SetDesc("Disallow a user from reading messages sent in the room.");
|
||||
fCommands.AddItem("deafen", deafen);
|
||||
|
||||
BMessage undeafenMsg(IM_MESSAGE);
|
||||
undeafenMsg.AddInt32("im_what", IM_ROOM_UNDEAFEN_PARTICIPANT);
|
||||
ChatCommand* undeafen = new ChatCommand("undeafen", undeafenMsg, true, roomUser);
|
||||
undeafen->SetDesc("Restore a user's ability to receive messages.");
|
||||
fCommands.AddItem("undeafen", undeafen);
|
||||
|
||||
BMessage inviteMsg(IM_MESSAGE);
|
||||
inviteMsg.AddInt32("im_what", IM_ROOM_SEND_INVITE);
|
||||
ChatCommand* invite = new ChatCommand("invite", inviteMsg, true, knownUser);
|
||||
invite->SetDesc("Invite a user to the current room.");
|
||||
fCommands.AddItem("invite", invite);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <libsupport/KeyMap.h>
|
||||
|
||||
#include "CayaConstants.h"
|
||||
#include "ChatCommand.h"
|
||||
#include "Contact.h"
|
||||
#include "Conversation.h"
|
||||
#include "ProtocolLooper.h"
|
||||
|
@ -58,6 +59,9 @@ public:
|
|||
Conversation* ConversationById(BString id, int64 instance);
|
||||
void AddConversation(Conversation* chat, int64 instance);
|
||||
|
||||
CommandMap Commands();
|
||||
ChatCommand* CommandById(BString id);
|
||||
|
||||
private:
|
||||
ProtocolLooper* _LooperFromMessage(BMessage* message);
|
||||
|
||||
|
@ -68,11 +72,13 @@ private:
|
|||
|
||||
Role* _GetRole(BMessage* msg);
|
||||
|
||||
void _InitDefaultCommands();
|
||||
void _ReplicantStatusNotify(CayaStatus status);
|
||||
|
||||
ProtocolLoopers fLoopers;
|
||||
AccountInstances
|
||||
fAccounts;
|
||||
CommandMap fCommands;
|
||||
BString fMySelf;
|
||||
};
|
||||
|
||||
|
|
|
@ -111,14 +111,13 @@ ConversationView::ImMessage(BMessage* msg)
|
|||
}
|
||||
case IM_MESSAGE_RECEIVED:
|
||||
{
|
||||
BString message = msg->FindString("body");
|
||||
BString id = msg->FindString("user_id");
|
||||
User* sender = fConversation->UserById(id);
|
||||
BString uname = sender->GetName();
|
||||
|
||||
// Send a notification, if it's appropriate
|
||||
if ((Window() == NULL || Window()->IsActive() == false)
|
||||
&& (!CayaPreferences::Item()->NotifyNewMessage))
|
||||
&& (!CayaPreferences::Item()->NotifyNewMessage)
|
||||
&& sender != NULL)
|
||||
{
|
||||
fMessageCount++;
|
||||
BString notify_message;
|
||||
|
@ -128,14 +127,14 @@ ConversationView::ImMessage(BMessage* msg)
|
|||
notify_message << " new message from ";
|
||||
else
|
||||
notify_message << " new messages from ";
|
||||
notify_message << uname;
|
||||
notify_message << sender->GetName();
|
||||
|
||||
BNotification notification(B_INFORMATION_NOTIFICATION);
|
||||
notification.SetGroup(BString("Caya"));
|
||||
notification.SetTitle(BString("New message"));
|
||||
notification.SetIcon(sender->AvatarBitmap());
|
||||
notification.SetContent(notify_message);
|
||||
notification.SetMessageID(uname);
|
||||
notification.SetMessageID(sender->GetName());
|
||||
notification.Send();
|
||||
// Check if the user want the notification
|
||||
if (!CayaPreferences::Item()->NotifyNewMessage)
|
||||
|
|
Ŝarĝante…
Reference in New Issue