Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/chat/components/ui/multiplayer-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export function MultiplayerActionsProvider({ children }: MultiplayerActionsProvi

const timestamp = new Date();
const message: ActionMessage = {
messageId: crypto.randomUUID(),
method,
userId,
name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,15 @@ export class ChatsManager {
multiplayerConnection.addEventListener('chat', async (e) => {
const { playerId, message } = e.data;
if (playerId !== agent.id) {
await conversation.addLocalMessage(message);
const playerSpec = conversation.agentsMap.get(playerId);
const agent = {
id: playerId,
name: playerSpec.name,
};
const formattedMessage = formatConversationMessage(message, {
agent,
});
await conversation.addLocalMessage(formattedMessage);
// } else {
// // XXX fix this
// console.warn('received own message from realms "chat" event; this should not happen', message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
PlayableAudioStream,
GetHashFn,
MessageCache,
ReplyFn,
} from '../types'
import { SceneObject } from '../classes/scene-object';
import { Player } from 'react-agents-client/util/player.mjs';
Expand All @@ -24,19 +25,22 @@ export class ConversationObject extends EventTarget {
getHash: GetHashFn; // XXX this can be a string, since conversation hashes do not change (?)
messageCache: MessageCache;
numTyping: number = 0;
replyFn: ReplyFn | null;
mentionsRegex: RegExp | null = null;

constructor({
agent,
agentsMap = new Map(),
scene = null,
getHash = () => '',
replyFn = null,
mentionsRegex = null,
}: {
agent: ActiveAgentObject | null;
agentsMap?: Map<string, Player>;
scene?: SceneObject | null;
getHash?: GetHashFn;
replyFn?: ReplyFn;
mentionsRegex?: RegExp | null;
}) {
super();
Expand All @@ -45,6 +49,7 @@ export class ConversationObject extends EventTarget {
this.agentsMap = agentsMap;
this.scene = scene;
this.getHash = getHash;
this.replyFn = replyFn;
this.mentionsRegex = mentionsRegex;
this.messageCache = new MessageCacheConstructor({
loader: async () => {
Expand Down Expand Up @@ -142,6 +147,10 @@ export class ConversationObject extends EventTarget {
].join('\n');
}

getMessageById(messageId: string) {
return this.messageCache.getMessageById(messageId);
}

getCachedMessages(filter?: MessageFilter) {
const agent = filter?.agent;
const idMatches = agent?.idMatches;
Expand Down Expand Up @@ -303,6 +312,13 @@ export class ConversationObject extends EventTarget {
return [] as ActionMessage[];
} */

async reply(message: ActionMessage, replyToMessageId: string) {
if (!this.replyFn) {
throw new Error('No reply function configured for this conversation');
}
await this.replyFn(message, replyToMessageId);
}

// handle a message from the network
async addLocalMessage(message: ActionMessage) {
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ExtendableMessageEvent,
ActionMessageEventData,
PlayableAudioStream,
ActionMessage,
} from '../types';
import {
ConversationObject,
Expand Down Expand Up @@ -80,7 +81,8 @@ const bindOutgoing = ({
if (method === 'say') {
let {
text,
} = args as { text: string };
replyToMessageId,
} = args as { text: string, replyToMessageId?: string };

if (attachments && Object.keys(attachments).length > 0) {
text += '\n' + Object.values(attachments)
Expand All @@ -93,6 +95,13 @@ const bindOutgoing = ({
text = conversation.formatOutgoingMessageMentions(text);
}


if (replyToMessageId) {
message.args.text = text;
conversation.reply(message, replyToMessageId);
return;
}

discordBotClient.input.writeText(text, {
channelId,
userId,
Expand Down Expand Up @@ -122,6 +131,36 @@ const bindOutgoing = ({
// });
};

const bindReply = ({
conversation,
discordBotClient,
channelId,
userId,
}: {
conversation: ConversationObject,
discordBotClient: DiscordBotClient,
channelId?: string,
userId?: string,
}) => {
conversation.replyFn = async (message: ActionMessage, replyToMessageId: string) => {
const replyMessage = conversation.getMessageById(replyToMessageId);
const { discordMessageId } = replyMessage?.args;
const { text } = message.args;

const replyObject = {
content: text,
reply: {
messageReference: discordMessageId,
},
};

discordBotClient.input.writeText(replyObject, {
channelId,
userId,
});
};
};

//

export class DiscordBot extends EventTarget {
Expand Down Expand Up @@ -260,6 +299,11 @@ export class DiscordBot extends EventTarget {
discordBotClient,
channelId,
});
bindReply({
conversation,
discordBotClient,
channelId,
});

// console.log('write text to channel', {
// channelId,
Expand Down Expand Up @@ -307,6 +351,11 @@ export class DiscordBot extends EventTarget {
discordBotClient,
userId,
});
bindReply({
conversation,
discordBotClient,
userId,
});

// console.log('write text to user', {
// userId,
Expand Down Expand Up @@ -362,6 +411,7 @@ export class DiscordBot extends EventTarget {
text,
channelId, // if there is no channelId, it's a DM
// XXX discord channel/dm distinction can be made more explicit with a type: string field...
messageId,
} = e.data;

// look up conversation
Expand All @@ -382,6 +432,7 @@ export class DiscordBot extends EventTarget {
method: 'say',
args: {
text: formattedMessage,
discordMessageId: messageId,
},
};
const id = getIdFromUserId(userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export class MessageCache extends EventTarget {

this.loader = loader;
}
getMessageById(messageId: string) {
return this.#messages.find((m) => m.messageId === messageId);
}
getMessages() {
return this.#messages;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,32 @@ export const ChatActions = () => {
type="say"
description={dedent`\
Say something in the chat.

If you want to mention a player, use the @userId format.

You should use replyToMessageId to reference a specific previous message, usage guidelines:
1. When you are directly tagged or mentioned in a message
2. When you need to reference a specific previous message according to the conversation context
3. In all other cases, send your message without a reply reference.

Not every message needs a reply reference so you should only use it when necessary
`}
schema={
z.object({
text: z.string(),
replyToMessageId: z.string().optional(),
})
}
examples={[
{
text: 'Hello, there! How are you doing?',

},
{
text: 'What are you talking about?',
},
{
text: 'Not much, what about you?',
replyToMessageId: '123',
},
]}
// handler={async (e: PendingActionEvent) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,10 @@ const CachedMessagesPrompt = () => {
'\n' +
cachedMessages
.map((action) => {
const { /*userId,*/ name, method, args, attachments = [], timestamp } = action;
const { /*userId,*/ messageId, name, method, args, attachments = [], timestamp } = action;
const j = {
// userId,
messageId,
name,
method,
args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,18 @@ export type Attachment = FormattedAttachment & {
url?: string;
};
export type ActionMessage = {
messageId: string;
userId: string;
name: string;
method: string;
args: any;
replyToMessageId?: string;
attachments?: Attachment[];
human: boolean; // XXX can be converted to flags
hidden: boolean;
timestamp: Date;
};
export type ReplyFn = (message: ActionMessage, replyToMessageId: string) => Promise<void>;
export type PendingActionMessage = {
method: string;
args: any;
Expand Down Expand Up @@ -296,6 +299,7 @@ export type Debouncer = EventTarget & {

export type MessageCache = EventTarget & {
getMessages(): ActionMessage[];
getMessageById(messageId: string): ActionMessage | undefined;
pushMessage(message: ActionMessage): Promise<void>;
// prependMessages(messages: ActionMessage[]): Promise<void>;
trim(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export async function loadMessagesFromDatabase({
limit,
}) {
const { error, data } = await supabase
.from( 'agent_messages' )
.from('agent_messages')
.select([
'id',
'method',
'args',
'attachments',
Expand All @@ -16,8 +17,8 @@ export async function loadMessagesFromDatabase({
].join(','))
.eq('user_id', agentId)
.eq('conversation_id', conversationId)
.order( 'created_at', { ascending: true })
.limit( limit );
.order('created_at', { ascending: true })
.limit(limit);
if (!error) {
return decodeMessages(data);
} else {
Expand All @@ -26,7 +27,8 @@ export async function loadMessagesFromDatabase({
}

function decodeMessages(messages) {
return messages.map( message => ({
return messages.map(message => ({
messageId: message.id,
method: message.method,
args: message.args,
attachments: message.attachments,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const formatConversationMessage = (rawMessage: PendingActionMessage, {
const { method, args, attachments } = rawMessage;
const timestamp = new Date();
const newMessage = {
messageId: crypto.randomUUID(),
userId,
name,
method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async function encodeMessage(message, jwt, userId, conversationId) {
args: message.args,
}), { jwt });
return {
id: message.messageId,
method: message.method,
args: message.args,
attachments: message.attachments,
Expand Down