Admin
This example shows how to manage your group metadata and members.
Commands
Here are the commands to add and remove users:
cmd
/add @alix
/remove @bo
/name "New Group Name"
Main code
src/handler/admin.ts
import { HandlerContext } from "@xmtp/message-kit";
import type { User } from "@xmtp/message-kit";
// Reusable function to handle adding members
function handleAddMembers(
addedInboxes: { inboxId: string }[],
members: User[],
) {
const addedNames = members
?.filter((member: User) =>
addedInboxes.some(
(added: { inboxId: string }) =>
added?.inboxId?.toLowerCase() === member?.inboxId?.toLowerCase(),
),
)
.map((member: User) => `@${member.username}`)
.filter((name: string) => name.trim() !== "@") // Filter out empty or undefined usernames
.join(", "); // Join names for message formatting
if (addedNames && addedNames.trim().length > 0) {
let messages = [
`Yo, ${addedNames}! ðŦĄ`,
`LFG ${addedNames}!`,
`${addedNames}ðĪ`,
];
return messages[Math.floor(Math.random() * messages.length)];
}
return "";
}
function handleRemoveMembers() {
let messages = [`ðŠĶ`, `ðŧ`, `hasta la vista, baby`];
return messages[Math.floor(Math.random() * messages.length)];
}
const handleGroupname = (newValue: string, adminName: string) => {
let messages = [`LFG '${newValue}'! ðĨ`, `all hail '${newValue}' ððŧ`];
return messages[Math.floor(Math.random() * messages.length)];
};
export async function handler(context: HandlerContext) {
const {
group,
members,
message: { content, typeId, sender },
} = context;
if (typeId === "group_updated") {
const {
initiatedByInboxId,
metadataFieldChanges,
removedInboxes,
addedInboxes,
} = content;
// Fetch username from members array mapped by inboxId
const adminName =
members?.find((member) => member.inboxId === initiatedByInboxId)
?.username || "Admin";
let message: string = "";
if (addedInboxes && addedInboxes.length > 0) {
message += handleAddMembers(addedInboxes, members!);
} else if (removedInboxes && removedInboxes.length > 0) {
message += handleRemoveMembers();
} else if (metadataFieldChanges && metadataFieldChanges[0]) {
const { fieldName, newValue } = metadataFieldChanges?.[0];
if (fieldName === "group_name") {
message += handleGroupname(newValue, adminName);
}
}
await context.reply(message);
} else if (typeId === "text") {
const {
params: { type, username, name },
} = content;
switch (type) {
case "remove":
try {
const removedInboxes = username.map((user: User) => user.inboxId);
if (!removedInboxes || removedInboxes.length === 0) {
context.reply("Wrong username");
return;
}
await group.sync();
await group.removeMembersByInboxId(removedInboxes);
const messages = handleRemoveMembers();
context.reply(messages);
} catch (error) {
context.reply("Error: Check admin privileges");
console.error(error);
}
break;
case "add":
try {
const addedInboxes = username.map((user: User) =>
user?.inboxId?.toLowerCase(),
);
if (!addedInboxes || addedInboxes.length === 0) {
context.reply("Wrong username");
return;
}
await group.sync();
await group.addMembersByInboxId(addedInboxes);
const messages = handleAddMembers(
[{ inboxId: addedInboxes[0] }],
members!,
);
context.reply(messages);
} catch (error) {
context.reply("Error: Check admin privileges");
console.error(error);
}
break;
}
}
return;
}