import * as ser from '../serialize.js'; const contactParam = { type: 'chat', required: true, description: 'Contact: JID (549...@c.us), phone number or exact name' }; export default { 'contacts.list': { description: 'Lists contacts (users; no groups).', params: { query: { type: 'string', description: 'Filters by name or number' }, saved: { type: 'boolean', description: 'true: only contacts saved on the phone' }, limit: { type: 'integer', description: 'Maximum to return' }, }, columns: ['id', 'displayName', 'number', 'isMyContact'], handler: async (ctx, p) => { const q = p.query && ctx.normalize(p.query); let list = (await ctx.client.getContacts()) .filter((c) => !c.isGroup && c.id.server !== 'broadcast') .filter((c) => p.saved === undefined || Boolean(c.isMyContact) === p.saved) .map(ser.contact) .filter((c) => !q || [c.name, c.pushname, c.shortName, c.number] .some((v) => v && ctx.normalize(v).includes(q))) .sort((a, b) => (a.displayName || '').localeCompare(b.displayName || '')); if (p.limit) list = list.slice(0, p.limit); return list; }, }, 'contacts.get': { description: 'Details of a contact (includes "about").', params: { contactId: contactParam }, handler: async (ctx, p) => { const c = await ctx.client.getContactById(p.contactId); const out = ser.contact(c); out.about = await c.getAbout().catch(() => null); return out; }, }, 'contacts.resolve': { description: 'Checks whether a number is on WhatsApp and returns its JID.', params: { number: { type: 'string', required: true, description: 'International number (e.g. +54 9 11 2233-4455)' } }, handler: async (ctx, p) => { const digits = p.number.replace(/\D/g, ''); const id = await ctx.client.getNumberId(digits); return { number: digits, registered: Boolean(id), id: id?._serialized ?? null }; }, }, 'contacts.picture': { description: 'Profile picture URL (if privacy allows it).', params: { contactId: contactParam }, handler: async (ctx, p) => ({ url: (await ctx.client.getProfilePicUrl(p.contactId)) ?? null }), }, 'groups.list': { description: 'Lists groups.', params: { query: { type: 'string', description: 'Filters by name' } }, columns: ['id', 'name', 'participantCount', 'unreadCount', 'date'], handler: async (ctx, p) => (await ctx.client.getChats()) .filter((c) => c.isGroup) .filter((c) => !p.query || ctx.normalize(c.name || '').includes(ctx.normalize(p.query))) .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0)) .map((c) => ser.chat(c, ctx.ser)), }, 'groups.get': { description: 'Details of a group: description, owner and participants.', params: { groupId: { ...contactParam, description: 'Grupo: JID (...@g.us) o nombre exacto' } }, handler: async (ctx, p) => { const c = await ctx.getChat(p.groupId); if (!c.isGroup) throw new ctx.WaError('EINVAL', `${p.groupId} is not a group`); return ser.group(c, ctx.ser); }, }, 'groups.invite': { description: 'Group invite link (requires being an admin).', params: { groupId: { ...contactParam, description: 'Grupo: JID (...@g.us) o nombre exacto' } }, handler: async (ctx, p) => { const c = await ctx.getChat(p.groupId); if (!c.isGroup) throw new ctx.WaError('EINVAL', `${p.groupId} is not a group`); const code = await c.getInviteCode(); return { code, link: `https://chat.whatsapp.com/${code}` }; }, }, };