This commit is contained in:
Matthias Hochmeister
2026-03-13 15:42:15 +01:00
parent 3dda069611
commit 75c919c063
13 changed files with 926 additions and 30 deletions

View File

@@ -139,6 +139,8 @@ interface NextcloudChatMessage {
messageType: string;
systemMessage: string;
messageParameters: Record<string, any>;
reactions: Record<string, any>;
reactionsSelf: string[];
}
async function getAllConversations(loginName: string, appPassword: string): Promise<NextcloudConversation[]> {
@@ -255,6 +257,8 @@ async function getMessages(token: string, loginName: string, appPassword: string
messageType: m.messageType ?? '',
systemMessage: m.systemMessage ?? '',
messageParameters: m.messageParameters ?? {},
reactions: m.reactions ?? {},
reactionsSelf: m.reactionsSelf ?? [],
}));
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
@@ -274,7 +278,7 @@ async function getMessages(token: string, loginName: string, appPassword: string
}
}
async function sendMessage(token: string, message: string, loginName: string, appPassword: string): Promise<void> {
async function sendMessage(token: string, message: string, loginName: string, appPassword: string, replyTo?: number): Promise<void> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
@@ -283,7 +287,7 @@ async function sendMessage(token: string, message: string, loginName: string, ap
try {
await httpClient.post(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v1/chat/${encodeURIComponent(token)}`,
{ message },
{ message, ...(replyTo !== undefined ? { replyTo } : {}) },
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
@@ -560,5 +564,224 @@ async function getFilePreview(
}
}
async function searchUsers(query: string, loginName: string, appPassword: string): Promise<any[]> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.get(
`${baseUrl}/ocs/v2.php/core/autocomplete/get?search=${encodeURIComponent(query)}&limit=20&itemType=&itemId=&shareTypes[]=0&shareTypes[]=1&shareTypes[]=7&format=json`,
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
},
},
);
return (response.data?.ocs?.data ?? []).map((u: any) => ({ id: u.id, label: u.label, source: u.source }));
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.searchUsers failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.searchUsers failed', { error });
throw new Error('Failed to search users');
}
}
async function createRoom(roomType: number, invite: string, roomName: string | undefined, loginName: string, appPassword: string): Promise<{ token: string }> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.post(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/room`,
{ roomType, invite, ...(roomName ? { roomName } : {}) },
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
},
);
return { token: response.data?.ocs?.data?.token as string };
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.createRoom failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.createRoom failed', { error });
throw new Error('Failed to create room');
}
}
async function addReaction(token: string, messageId: number, reaction: string, loginName: string, appPassword: string): Promise<void> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
await httpClient.post(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v1/reaction/${encodeURIComponent(token)}/${messageId}`,
{ reaction },
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
},
);
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.addReaction failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.addReaction failed', { error });
throw new Error('Failed to add reaction');
}
}
async function removeReaction(token: string, messageId: number, reaction: string, loginName: string, appPassword: string): Promise<void> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
await httpClient.delete(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v1/reaction/${encodeURIComponent(token)}/${messageId}`,
{
params: { reaction },
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
},
},
);
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.removeReaction failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.removeReaction failed', { error });
throw new Error('Failed to remove reaction');
}
}
async function getReactions(token: string, messageId: number, loginName: string, appPassword: string): Promise<Record<string, any>> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.get(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v1/reaction/${encodeURIComponent(token)}/${messageId}`,
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
},
},
);
return response.data?.ocs?.data ?? {};
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.getReactions failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.getReactions failed', { error });
throw new Error('Failed to get reactions');
}
}
async function getPollDetails(token: string, pollId: number, loginName: string, appPassword: string): Promise<Record<string, any>> {
const baseUrl = environment.nextcloudUrl;
if (!baseUrl || !isValidServiceUrl(baseUrl)) {
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.get(
`${baseUrl}/ocs/v2.php/apps/spreed/api/v1/poll/${encodeURIComponent(token)}/${pollId}`,
{
headers: {
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
'OCS-APIRequest': 'true',
'Accept': 'application/json',
},
},
);
return response.data?.ocs?.data ?? {};
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
const err = new Error('Nextcloud authentication invalid');
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
throw err;
}
if (axios.isAxiosError(error)) {
logger.error('NextcloudService.getPollDetails failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
}
logger.error('NextcloudService.getPollDetails failed', { error });
throw new Error('Failed to get poll details');
}
}
export type { NextcloudChatMessage, GetMessagesOptions };
export default { initiateLoginFlow, pollLoginFlow, getConversations, getAllConversations, getMessages, sendMessage, markAsRead, uploadFileToTalk, downloadFile, getFilePreview };
export default { initiateLoginFlow, pollLoginFlow, getConversations, getAllConversations, getMessages, sendMessage, markAsRead, uploadFileToTalk, downloadFile, getFilePreview, searchUsers, createRoom, addReaction, removeReaction, getReactions, getPollDetails };