Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v0.7.23 135 lines 3.7 kB view raw
1import { ChatMessageViewHydrated } from "streamplace"; 2 3export enum SystemMessageType { 4 stream_start = "stream_start", 5 stream_end = "stream_end", 6 notification = "notification", 7} 8 9export interface SystemMessageMetadata { 10 username?: string; 11 action?: string; 12 count?: number; 13 duration?: string; 14 reason?: string; 15 streamerName?: string; 16} 17 18/** 19 * Creates a system message with the proper structure 20 * @param type The type of system message 21 * @param text The message text 22 * @param metadata Optional metadata for the message 23 * @returns A properly formatted ChatMessageViewHydrated object 24 */ 25export const createSystemMessage = ( 26 type: SystemMessageType, 27 text: string, 28 metadata?: SystemMessageMetadata, 29 date: Date = new Date(), 30): ChatMessageViewHydrated => { 31 const now = date; 32 33 return { 34 uri: `at://did:sys:system/place.stream.chat.message/${now.getTime()}`, 35 cid: `system-${now.getTime()}`, 36 author: { 37 did: "did:sys:system", 38 handle: type, // Use handle to specify the type of system message 39 }, 40 record: { 41 text, 42 createdAt: now.toISOString(), 43 streamer: "system", 44 $type: "place.stream.chat.message", 45 }, 46 indexedAt: now.toISOString(), 47 chatProfile: { 48 color: { red: 128, green: 128, blue: 128 }, // Gray color for system messages 49 }, 50 }; 51}; 52 53/** 54 * System message factory functions for common scenarios 55 */ 56export const SystemMessages = { 57 streamStart: (streamerName: string): ChatMessageViewHydrated => 58 createSystemMessage( 59 SystemMessageType.stream_start, 60 `Now streaming - ${streamerName}`, 61 { 62 streamerName, 63 }, 64 ), 65 66 // technically, streams can't 'end' on Streamplace 67 // possibly we could use deleting or editing streams (`endedAt` param) for this? 68 streamEnd: (duration?: string): ChatMessageViewHydrated => 69 createSystemMessage( 70 SystemMessageType.stream_end, 71 duration ? `Stream has ended. Duration: ${duration}` : "Stream has ended", 72 { duration }, 73 ), 74 75 notification: (message: string): ChatMessageViewHydrated => 76 createSystemMessage(SystemMessageType.notification, message), 77}; 78 79/** 80 * Checks if a message is a system message 81 * @param message The message to check 82 * @returns True if the message is a system message 83 */ 84export const isSystemMessage = (message: ChatMessageViewHydrated): boolean => { 85 return message.author.did === "did:sys:system"; 86}; 87 88/** 89 * Gets the system message type from a message 90 * @param message The message to check 91 * @returns The system message type or null if not a system message 92 */ 93export const getSystemMessageType = ( 94 message: ChatMessageViewHydrated, 95): SystemMessageType | null => { 96 if (!isSystemMessage(message)) { 97 return null; 98 } 99 return message.author.handle as SystemMessageType; 100}; 101 102/** 103 * Parses metadata from a system message based on its type 104 * @param message The system message to parse 105 * @returns The parsed metadata 106 */ 107export const parseSystemMessageMetadata = ( 108 message: ChatMessageViewHydrated, 109): SystemMessageMetadata => { 110 const metadata: SystemMessageMetadata = {}; 111 const type = getSystemMessageType(message); 112 const text = message.record.text; 113 114 if (!type) return metadata; 115 116 switch (type) { 117 case "stream_end": { 118 const durationMatch = text.match(/Duration:\s*(\d+:\d+(?::\d+)?)/); 119 if (durationMatch) { 120 metadata.duration = durationMatch[1]; 121 } 122 break; 123 } 124 125 case "stream_start": { 126 const streamerMatch = text.match(/^(.+?)\s+is now live!/); 127 if (streamerMatch) { 128 metadata.streamerName = streamerMatch[1]; 129 } 130 break; 131 } 132 } 133 134 return metadata; 135};