import type { NewsletterCreateResponse, SocketConfig, WAMediaUpload } from '../Types' import type { NewsletterMetadata, NewsletterUpdate } from '../Types' import { QueryIds, XWAPaths } from '../Types' import { generateProfilePicture } from '../Utils/messages-media' import { getBinaryNodeChild } from '../WABinary' import { makeGroupsSocket } from './groups' import { executeWMexQuery as genericExecuteWMexQuery } from './mex' const parseNewsletterCreateResponse = (response: NewsletterCreateResponse): NewsletterMetadata => { // Validate response structure before destructuring if (!response?.id || !response?.thread_metadata) { throw new Error('Invalid newsletter response: missing id or thread_metadata') } const { id, thread_metadata: thread, viewer_metadata: viewer } = response // Validate required thread metadata fields if (!thread.name?.text) { throw new Error('Invalid newsletter response: missing thread name') } return { id: id, owner: undefined, name: thread.name.text, creation_time: parseInt(thread.creation_time, 10) || 0, description: thread.description?.text || '', invite: thread.invite || '', subscribers: parseInt(thread.subscribers_count, 10) || 0, verification: thread.verification, picture: thread.picture ? { id: thread.picture.id || '', directPath: thread.picture.direct_path || '' } : { id: '', directPath: '' }, mute_state: viewer?.mute } } const parseNewsletterMetadata = (result: unknown): NewsletterMetadata | null => { if (typeof result !== 'object' || result === null) { return null } if ('id' in result && typeof result.id === 'string') { return result as NewsletterMetadata } if ('result' in result && typeof result.result === 'object' && result.result !== null && 'id' in result.result) { return result.result as NewsletterMetadata } return null } export const makeNewsletterSocket = (config: SocketConfig) => { const sock = makeGroupsSocket(config) const { query, generateMessageTag } = sock const executeWMexQuery = (variables: Record, queryId: string, dataPath: string): Promise => { return genericExecuteWMexQuery(variables, queryId, dataPath, query, generateMessageTag) } const newsletterUpdate = async (jid: string, updates: NewsletterUpdate) => { const variables = { newsletter_id: jid, updates: { ...updates, settings: null } } return executeWMexQuery(variables, QueryIds.UPDATE_METADATA, 'xwa2_newsletter_update') } return { ...sock, newsletterCreate: async (name: string, description?: string): Promise => { const variables = { input: { name, description: description ?? null } } const rawResponse = await executeWMexQuery( variables, QueryIds.CREATE, XWAPaths.xwa2_newsletter_create ) return parseNewsletterCreateResponse(rawResponse) }, newsletterUpdate, newsletterSubscribers: async (jid: string) => { return executeWMexQuery<{ subscribers: number }>( { newsletter_id: jid }, QueryIds.SUBSCRIBERS, XWAPaths.xwa2_newsletter_subscribers ) }, newsletterMetadata: async (type: 'invite' | 'jid', key: string) => { const variables = { fetch_creation_time: true, fetch_full_image: true, fetch_viewer_metadata: true, input: { key, type: type.toUpperCase() } } const result = await executeWMexQuery(variables, QueryIds.METADATA, XWAPaths.xwa2_newsletter_metadata) return parseNewsletterMetadata(result) }, newsletterFollow: (jid: string) => { return executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_follow) }, newsletterUnfollow: (jid: string) => { return executeWMexQuery({ newsletter_id: jid }, QueryIds.UNFOLLOW, XWAPaths.xwa2_newsletter_unfollow) }, newsletterMute: (jid: string) => { return executeWMexQuery({ input: { newsletter_id: jid, type: 'MUTE_ADMIN_ACTIVITY', value: 'OFF' } }, QueryIds.MUTE, XWAPaths.xwa2_newsletter_mute_v2) }, newsletterUnmute: (jid: string) => { return executeWMexQuery({ input: { newsletter_id: jid, type: 'MUTE_ADMIN_ACTIVITY', value: 'ON' } }, QueryIds.UNMUTE, XWAPaths.xwa2_newsletter_unmute_v2) }, newsletterUpdateName: async (jid: string, name: string) => { return await newsletterUpdate(jid, { name }) }, newsletterUpdateDescription: async (jid: string, description: string) => { return await newsletterUpdate(jid, { description }) }, newsletterUpdatePicture: async (jid: string, content: WAMediaUpload) => { const { img } = await generateProfilePicture(content) return await newsletterUpdate(jid, { picture: img.toString('base64') }) }, newsletterRemovePicture: async (jid: string) => { return await newsletterUpdate(jid, { picture: '' }) }, newsletterReactMessage: async (jid: string, serverId: string, reaction?: string) => { await query({ tag: 'message', attrs: { to: jid, ...(reaction ? {} : { edit: '7' }), type: 'reaction', server_id: serverId, id: generateMessageTag() }, content: [ { tag: 'reaction', attrs: reaction ? { code: reaction } : {} } ] }) }, newsletterFetchMessages: async (jid: string, count: number, since: number, after: number) => { const messageUpdateAttrs: { count: string; since?: string; after?: string } = { count: count.toString() } if (typeof since === 'number') { messageUpdateAttrs.since = since.toString() } if (after) { messageUpdateAttrs.after = after.toString() } const result = await query({ tag: 'iq', attrs: { id: generateMessageTag(), type: 'get', xmlns: 'newsletter', to: jid }, content: [ { tag: 'message_updates', attrs: messageUpdateAttrs } ] }) return result }, subscribeNewsletterUpdates: async (jid: string): Promise<{ duration: string } | null> => { const result = await query({ tag: 'iq', attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid }, content: [{ tag: 'live_updates', attrs: {}, content: [] }] }) const liveUpdatesNode = getBinaryNodeChild(result, 'live_updates') const duration = liveUpdatesNode?.attrs?.duration return duration ? { duration: duration } : null }, newsletterAdminCount: async (jid: string): Promise => { const response = await executeWMexQuery<{ admin_count: number }>( { newsletter_id: jid }, QueryIds.ADMIN_COUNT, XWAPaths.xwa2_newsletter_admin_count ) return response.admin_count }, newsletterChangeOwner: async (jid: string, newOwnerJid: string) => { await executeWMexQuery( { newsletter_id: jid, user_id: newOwnerJid }, QueryIds.CHANGE_OWNER, XWAPaths.xwa2_newsletter_change_owner ) }, newsletterDemote: async (jid: string, userJid: string) => { await executeWMexQuery({ newsletter_id: jid, user_id: userJid }, QueryIds.DEMOTE, XWAPaths.xwa2_newsletter_demote) }, newsletterDelete: async (jid: string) => { await executeWMexQuery({ newsletter_id: jid }, QueryIds.DELETE, XWAPaths.xwa2_newsletter_delete_v2) } } } export type NewsletterSocket = ReturnType