-
Notifications
You must be signed in to change notification settings - Fork 9
RG-T117 Chat and Chatbot Entrypoints #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| allowBuilds: | ||
| '@react-buoy/shared-ui': set this to true or false | ||
| '@sentry/cli': set this to true or false | ||
| electron: set this to true or false | ||
| electron-winstaller: set this to true or false | ||
| postinstall-postinstall: set this to true or false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,255 @@ | ||
| import { getBaseApiUrl } from '@/lib/storage/app'; | ||
| import { | ||
| type AddMembersInput, | ||
| type AddReactionInput, | ||
| type ChatAckResultData, | ||
| type ChatActionResult, | ||
| type ChatAttachmentUploadedResult, | ||
| type ChatChannelResultData, | ||
| type ChatMemberResultData, | ||
| type ChatMessageResultData, | ||
| type ChatV4Response, | ||
| type CreateAdHocChannelInput, | ||
| type CreateDirectMessageInput, | ||
| type EditMessageInput, | ||
| type FlagMessageInput, | ||
| type GetChatPresenceResult, | ||
| type GifResultData, | ||
| type MarkReadInput, | ||
| type SendChatMessageInput, | ||
| type SetNotificationPreferenceInput, | ||
| type UpdateChannelInput, | ||
| } from '@/models/v4/chat'; | ||
| import useAuthStore from '@/stores/auth/store'; | ||
|
|
||
| import { api } from '../common/client'; | ||
|
|
||
| const CHAT = '/Chat'; | ||
| const MODERATION = '/ChatModeration'; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Channels | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export const getChannels = async (activeUnitId?: number, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatChannelResultData[]>>(`${CHAT}/GetChannels`, { | ||
| params: activeUnitId != null ? { activeUnitId } : undefined, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getChannel = async (channelId: string, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatChannelResultData>>(`${CHAT}/GetChannel`, { params: { channelId }, signal }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const createDirectMessage = async (input: CreateDirectMessageInput) => { | ||
| const response = await api.post<ChatV4Response<ChatChannelResultData>>(`${CHAT}/CreateDirectMessage`, input); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const createAdHocChannel = async (input: CreateAdHocChannelInput) => { | ||
| const response = await api.post<ChatV4Response<ChatChannelResultData>>(`${CHAT}/CreateAdHocChannel`, input); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const updateChannel = async (channelId: string, input: UpdateChannelInput) => { | ||
| const response = await api.put<ChatV4Response<ChatChannelResultData>>(`${CHAT}/UpdateChannel`, input, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const archiveChannel = async (channelId: string) => { | ||
| const response = await api.delete<ChatActionResult>(`${CHAT}/ArchiveChannel`, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Members | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export const getMembers = async (channelId: string, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMemberResultData[]>>(`${CHAT}/GetMembers`, { params: { channelId }, signal }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const addMembers = async (channelId: string, input: AddMembersInput) => { | ||
| const response = await api.post<ChatV4Response<ChatMemberResultData[]>>(`${CHAT}/AddMembers`, input, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const removeMember = async (channelId: string, userId: string) => { | ||
| const response = await api.delete<ChatActionResult>(`${CHAT}/RemoveMember`, { params: { channelId, userId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const setNotificationPreference = async (channelId: string, input: SetNotificationPreferenceInput) => { | ||
| const response = await api.put<ChatActionResult>(`${CHAT}/SetNotificationPreference`, input, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Messages | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export const getMessages = async (channelId: string, beforeSeq?: number, limit = 50, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMessageResultData[]>>(`${CHAT}/GetMessages`, { | ||
| params: { channelId, beforeSeq, limit }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getMessagesAfter = async (channelId: string, afterSeq: number, limit = 50, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMessageResultData[]>>(`${CHAT}/GetMessagesAfter`, { | ||
| params: { channelId, afterSeq, limit }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getThread = async (messageId: string, beforeSeq?: number, limit = 50, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMessageResultData[]>>(`${CHAT}/GetThread`, { | ||
| params: { messageId, beforeSeq, limit }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const sendMessage = async (channelId: string, input: SendChatMessageInput) => { | ||
| const response = await api.post<ChatV4Response<ChatMessageResultData>>(`${CHAT}/SendMessage`, input, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const editMessage = async (messageId: string, input: EditMessageInput) => { | ||
| const response = await api.put<ChatV4Response<ChatMessageResultData>>(`${CHAT}/EditMessage`, input, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const deleteMessage = async (messageId: string) => { | ||
| const response = await api.delete<ChatActionResult>(`${CHAT}/DeleteMessage`, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Reactions, acks, read pointers, pins | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export const addReaction = async (messageId: string, input: AddReactionInput) => { | ||
| const response = await api.post<ChatActionResult>(`${CHAT}/AddReaction`, input, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const removeReaction = async (messageId: string, emoji: string) => { | ||
| const response = await api.delete<ChatActionResult>(`${CHAT}/RemoveReaction`, { params: { messageId, emoji } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const ackMessage = async (messageId: string) => { | ||
| const response = await api.post<ChatActionResult>(`${CHAT}/Ack`, {}, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getMyPendingAcks = async (signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatAckResultData[]>>(`${CHAT}/GetMyPendingAcks`, { signal }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const markRead = async (channelId: string, input: MarkReadInput) => { | ||
| const response = await api.put<ChatActionResult>(`${CHAT}/MarkRead`, input, { params: { channelId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const pinMessage = async (messageId: string) => { | ||
| const response = await api.post<ChatActionResult>(`${CHAT}/PinMessage`, {}, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const unpinMessage = async (messageId: string) => { | ||
| const response = await api.delete<ChatActionResult>(`${CHAT}/UnpinMessage`, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getPins = async (channelId: string, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMessageResultData[]>>(`${CHAT}/GetPins`, { params: { channelId }, signal }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Attachments | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export interface ChatUploadFile { | ||
| uri: string; | ||
| name: string; | ||
| type: string; | ||
| } | ||
|
|
||
| export const uploadAttachment = async (channelId: string, messageId: string, file: ChatUploadFile) => { | ||
| const form = new FormData(); | ||
| // React Native FormData accepts { uri, name, type } file objects. | ||
| form.append('file', file as unknown as Blob); | ||
|
|
||
| const response = await api.post<ChatAttachmentUploadedResult>(`${CHAT}/UploadAttachment`, form, { | ||
| params: { channelId, messageId }, | ||
| headers: { 'Content-Type': 'multipart/form-data' }, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| /** Absolute URL for downloading an attachment's binary. */ | ||
| export const getChatAttachmentUrl = (attachmentId: string): string => `${getBaseApiUrl()}${CHAT}/GetAttachment?attachmentId=${encodeURIComponent(attachmentId)}`; | ||
|
|
||
| /** Absolute URL for downloading an attachment's thumbnail. */ | ||
| export const getChatAttachmentThumbnailUrl = (attachmentId: string): string => `${getBaseApiUrl()}${CHAT}/GetAttachmentThumbnail?attachmentId=${encodeURIComponent(attachmentId)}`; | ||
|
|
||
| /** | ||
| * Image source (with bearer auth header) suitable for expo-image / RN Image | ||
| * when rendering a chat attachment. | ||
| */ | ||
| export const getChatAttachmentImageSource = (attachmentId: string) => { | ||
| const token = useAuthStore.getState().accessToken; | ||
| return { | ||
| uri: getChatAttachmentUrl(attachmentId), | ||
| headers: token ? { Authorization: `Bearer ${token}` } : undefined, | ||
| }; | ||
| }; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Search, GIFs, presence, flags, moderation | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| export const searchMessages = async (q: string, channelId?: string, page = 0, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<ChatMessageResultData[]>>(`${CHAT}/Search`, { | ||
| params: { q, channelId, page }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const searchGifs = async (q?: string, limit = 25, offset = 0, signal?: AbortSignal) => { | ||
| const response = await api.get<ChatV4Response<GifResultData[]>>(`${CHAT}/SearchGifs`, { | ||
| params: { q, limit, offset }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const getPresence = async (userIds: string[], signal?: AbortSignal) => { | ||
| const response = await api.get<GetChatPresenceResult>(`${CHAT}/GetPresence`, { | ||
| params: { userIds: userIds.join(',') }, | ||
| signal, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export const flagMessage = async (messageId: string, input: FlagMessageInput) => { | ||
| const response = await api.post<ChatActionResult>(`${CHAT}/FlagMessage`, input, { params: { messageId } }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| /** Department-admin / moderator hard delete of a message. */ | ||
| export const moderatorDeleteMessage = async (messageId: string, reason: string) => { | ||
| const response = await api.post<ChatActionResult>(`${MODERATION}/DeleteMessage`, {}, { params: { messageId, reason } }); | ||
| return response.data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { type ChatbotChannelResponse, type ChatbotSendResponse, type ChatbotSessionResponse } from '@/models/v4/chat'; | ||
|
|
||
| import { api } from '../common/client'; | ||
|
|
||
| const CHATBOT = '/Chatbot'; | ||
|
|
||
| /** Gets (creating if needed) the caller's chatbot conversation channel. */ | ||
| export const getChatbotChannel = async (signal?: AbortSignal) => { | ||
| const response = await api.get<ChatbotChannelResponse>(`${CHATBOT}/GetChatChannel`, { signal }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| /** | ||
| * Sends a message to the chatbot. The reply arrives asynchronously in the same | ||
| * channel over SignalR (chatbotMessageReceived). Idempotent via clientMessageId. | ||
| */ | ||
| export const sendChatbotMessage = async (text: string, clientMessageId: string) => { | ||
| const response = await api.post<ChatbotSendResponse>(`${CHATBOT}/SendChatMessage`, { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unhandled external HTTP POST call in Kody rule violation: Add try-catch blocks for external calls Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
| Text: text, | ||
| ClientMessageId: clientMessageId, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| /** Resets the chatbot conversational session (message history is retained). */ | ||
| export const newChatbotSession = async () => { | ||
| const response = await api.post<ChatbotSessionResponse>(`${CHATBOT}/NewChatSession`, {}); | ||
| return response.data; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JSDoc on
getChatbotChannelomits Promise return type and rejection conditions, violating documentation rules. Add@returns {Promise<ChatbotChannelResponse>}and document network/auth rejection conditions.Kody rule violation: Document async/Promise behavior and errors
Prompt for LLM
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.