Merge pull request #3494 from joaojmendes/OpenAIChatGPT
ChatGPT-APP, Update
This commit is contained in:
commit
7f0b96dd66
Binary file not shown.
Binary file not shown.
|
@ -101,6 +101,7 @@ This assume you have a Azure App called "OpenAIFunctionsApp", you can change thi
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
1.0.0|Feb 19, 2023|Initial release
|
1.0.0|Feb 19, 2023|Initial release
|
||||||
|
1.1.0|March 2, 2023|Update ChatGPT-APP to use the latest API and model ChatGPT-3.5-turbo
|
||||||
|
|
||||||
## Minimal Path to Awesome
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"This App is a implementation of OpenAI ChatGPT-3. It runs on SharePoint, Teams as Personal or Teams App and Message Extension."
|
"This App is a implementation of OpenAI ChatGPT-3. It runs on SharePoint, Teams as Personal or Teams App and Message Extension."
|
||||||
],
|
],
|
||||||
"creationDateTime": "2023-02-19",
|
"creationDateTime": "2023-02-19",
|
||||||
"updateDateTime": "2023-02-19",
|
"updateDateTime": "2023-03-02",
|
||||||
"products": [
|
"products": [
|
||||||
"SharePoint"
|
"SharePoint"
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export const CARD = {
|
export const CARD = {
|
||||||
"type": "AdaptiveCard",
|
"type": "AdaptiveCard",
|
||||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||||
"version": "1.2",
|
"version": "1.3",
|
||||||
"body": [
|
"body": [
|
||||||
{
|
{
|
||||||
"type": "ColumnSet",
|
"type": "ColumnSet",
|
||||||
|
@ -56,6 +56,21 @@ export const CARD = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "Container",
|
||||||
|
"spacing": "medium",
|
||||||
|
"style": "emphasis",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "TextBlock",
|
||||||
|
"text": "${$root.question}",
|
||||||
|
"wrap": true,
|
||||||
|
"horizontalAlignment": "Right",
|
||||||
|
"color": "Accent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"$when": "${length($root.question)>0}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "Container",
|
"type": "Container",
|
||||||
"spacing": "Padding",
|
"spacing": "Padding",
|
||||||
|
|
|
@ -26,14 +26,19 @@ export const ChatGptControl: React.FunctionComponent<IChatGptProps> = (
|
||||||
const { context } = props;
|
const { context } = props;
|
||||||
const [appGlobalState, setAppGlobalState] = useAtom(globalState);
|
const [appGlobalState, setAppGlobalState] = useAtom(globalState);
|
||||||
const { containerStyles } = useChatGptStyles();
|
const { containerStyles } = useChatGptStyles();
|
||||||
const { hasTeamsContext, chatId } = appGlobalState;
|
const { hasTeamsContext, chatId, channelId } = appGlobalState;
|
||||||
const { getTenantProperty } = useSpAPI(context);
|
const { getTenantProperty } = useSpAPI(context);
|
||||||
const [isLoading, setIsLoading] = React.useState(true);
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
const [error, setError] = React.useState<Error | undefined>(undefined);
|
const [error, setError] = React.useState<Error | undefined>(undefined);
|
||||||
|
|
||||||
const isInChat = React.useMemo((): boolean => {
|
const isInChat = React.useMemo((): boolean => {
|
||||||
return hasTeamsContext && !!chatId;
|
if (hasTeamsContext && (chatId )) {
|
||||||
}, [chatId, hasTeamsContext]);
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, [chatId, channelId, hasTeamsContext]);
|
||||||
|
|
||||||
|
const isInChannel = React.useMemo(() => !!channelId, [ channelId]);
|
||||||
|
|
||||||
const isPreviewChatId = React.useMemo((): boolean => {
|
const isPreviewChatId = React.useMemo((): boolean => {
|
||||||
if (isInChat) {
|
if (isInChat) {
|
||||||
|
@ -91,7 +96,7 @@ export const ChatGptControl: React.FunctionComponent<IChatGptProps> = (
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack tokens={{ childrenGap: 20 }} styles={containerStyles}>
|
<Stack tokens={{ childrenGap: 20 }} styles={containerStyles}>
|
||||||
<Header isInChat={isInChat} />
|
<Header isInChat={isInChat || isInChannel} />
|
||||||
<RenderPreviewChatInfo isPreviewChatId={isPreviewChatId} />
|
<RenderPreviewChatInfo isPreviewChatId={isPreviewChatId} />
|
||||||
<RenderMessages isShowMessages={!isPreviewChatId} />
|
<RenderMessages isShowMessages={!isPreviewChatId} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
@ -14,7 +14,6 @@ import { showNotification } from '@mantine/notifications';
|
||||||
import { CARD } from '../../adaptiveCards/chatGPTAnswerCard';
|
import { CARD } from '../../adaptiveCards/chatGPTAnswerCard';
|
||||||
import { globalState } from '../../atoms';
|
import { globalState } from '../../atoms';
|
||||||
import { useAdaptiveCardsUtils } from '../../hooks/useAdaptiveCardsUtils';
|
import { useAdaptiveCardsUtils } from '../../hooks/useAdaptiveCardsUtils';
|
||||||
import { useGraphAPI } from '../../hooks/useGraphAPI';
|
|
||||||
import { useSendMessageToTeams } from '../../hooks/useSendMessageToTeams';
|
import { useSendMessageToTeams } from '../../hooks/useSendMessageToTeams';
|
||||||
import { IAdaptativeCardData } from '../../models/IAdaptivecardData';
|
import { IAdaptativeCardData } from '../../models/IAdaptivecardData';
|
||||||
import { IRenderAnswerProps } from '../../models/IRenderAnswerProps';
|
import { IRenderAnswerProps } from '../../models/IRenderAnswerProps';
|
||||||
|
@ -26,24 +25,25 @@ import { SendMessageToChat } from '../SendMessageToChat/SendMessageToChat';
|
||||||
export const RenderAnswer: React.FunctionComponent<IRenderAnswerProps> = (
|
export const RenderAnswer: React.FunctionComponent<IRenderAnswerProps> = (
|
||||||
props: React.PropsWithChildren<IRenderAnswerProps>
|
props: React.PropsWithChildren<IRenderAnswerProps>
|
||||||
) => {
|
) => {
|
||||||
const { answer } = props;
|
const { answer, question } = props;
|
||||||
const { answerStyles, nameStyles, answerContainerStyles, controlStyles } = useChatGptStyles();
|
const { answerStyles, nameStyles, answerContainerStyles, controlStyles } = useChatGptStyles();
|
||||||
const [appGlobalState] = useAtom(globalState);
|
const [appGlobalState] = useAtom(globalState);
|
||||||
const { lastConversation, context, chatId } = appGlobalState;
|
const { lastConversation, context, chatId, teamsId, channelId, parentMessageId, hasTeamsContext } = appGlobalState;
|
||||||
const [error, setError] = React.useState<Error | undefined>(undefined);
|
const [error, setError] = React.useState<Error | undefined>(undefined);
|
||||||
const { sendMessage } = useGraphAPI(context);
|
|
||||||
const { createAdaptiveCard } = useAdaptiveCardsUtils();
|
const { createAdaptiveCard } = useAdaptiveCardsUtils();
|
||||||
const { sendAdativeCardToUsers } = useSendMessageToTeams(context);
|
const { sendAdativeCardToUsers } = useSendMessageToTeams(context);
|
||||||
const hasError = React.useMemo(() => error !== undefined, [error]);
|
const hasError = React.useMemo(() => error !== undefined, [error]);
|
||||||
|
|
||||||
|
|
||||||
const onSendMessageToChat = React.useCallback(async () => {
|
const onSendMessageToChat = React.useCallback(async () => {
|
||||||
if (answer && chatId) {
|
if (answer && hasTeamsContext ) {
|
||||||
try {
|
try {
|
||||||
const cardData: IAdaptativeCardData = { date: format(new Date(), "PPpp"), answer: answer };
|
const cardData: IAdaptativeCardData = { date: format(new Date(), "PPpp"), answer: answer, question: question ?? ""};
|
||||||
const card = createAdaptiveCard(cardData, CARD);
|
const card = createAdaptiveCard(cardData, CARD);
|
||||||
console.log("carddata", cardData);
|
console.log("carddata", cardData);
|
||||||
console.log("card", card);
|
console.log("card", card);
|
||||||
await sendAdativeCardToUsers(card, cardData, chatId);
|
await sendAdativeCardToUsers(card, cardData, chatId, teamsId, channelId, parentMessageId);
|
||||||
|
|
||||||
showNotification({
|
showNotification({
|
||||||
title: strings.ChatGPTAppNotificationTitle,
|
title: strings.ChatGPTAppNotificationTitle,
|
||||||
|
@ -57,7 +57,7 @@ export const RenderAnswer: React.FunctionComponent<IRenderAnswerProps> = (
|
||||||
setError(error);
|
setError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [answer, chatId, sendMessage, sendAdativeCardToUsers, createAdaptiveCard]);
|
}, [answer,teamsId,channelId, hasTeamsContext, chatId, sendAdativeCardToUsers, createAdaptiveCard, question, parentMessageId]);
|
||||||
|
|
||||||
const islastConversation = React.useMemo(() => lastConversation === "answer", [lastConversation]);
|
const islastConversation = React.useMemo(() => lastConversation === "answer", [lastConversation]);
|
||||||
return (
|
return (
|
||||||
|
@ -86,7 +86,6 @@ export const RenderAnswer: React.FunctionComponent<IRenderAnswerProps> = (
|
||||||
<SendMessageToChat onSendMessage={onSendMessageToChat} />
|
<SendMessageToChat onSendMessage={onSendMessageToChat} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack horizontalAlign="start" tokens={{ childrenGap: 10 }}>
|
<Stack horizontalAlign="start" tokens={{ childrenGap: 10 }}>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: answer?.replace("\n\n", " ") }}
|
dangerouslySetInnerHTML={{ __html: answer?.replace("\n\n", " ") }}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable require-atomic-updates */
|
||||||
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
|
@ -6,8 +8,12 @@ import { IIconProps } from 'office-ui-fabric-react/lib/Icon';
|
||||||
import { Stack } from 'office-ui-fabric-react/lib/Stack';
|
import { Stack } from 'office-ui-fabric-react/lib/Stack';
|
||||||
import { TextField } from 'office-ui-fabric-react/lib/TextField';
|
import { TextField } from 'office-ui-fabric-react/lib/TextField';
|
||||||
|
|
||||||
|
import { ChatMessage } from '@microsoft/microsoft-graph-types';
|
||||||
|
|
||||||
import { globalState } from '../../atoms/globalState';
|
import { globalState } from '../../atoms/globalState';
|
||||||
import { useChatGpt } from '../../hooks';
|
import { useChatGpt } from '../../hooks';
|
||||||
|
import { useGraphAPI } from '../../hooks/useGraphAPI';
|
||||||
|
import { useHtmlUtils } from '../../hooks/useHtmlUtils';
|
||||||
import { useChatGptStyles } from '../ChatGpt/useChatGptStyles';
|
import { useChatGptStyles } from '../ChatGpt/useChatGptStyles';
|
||||||
import { ErrorMessage } from '../ErrorMessage/ErrorMessage';
|
import { ErrorMessage } from '../ErrorMessage/ErrorMessage';
|
||||||
import { Loading } from '../LoadingAnswer/Loading';
|
import { Loading } from '../LoadingAnswer/Loading';
|
||||||
|
@ -23,7 +29,7 @@ export const RenderMessages: React.FunctionComponent<IRenderMessagesProps> = (
|
||||||
) => {
|
) => {
|
||||||
const { isShowMessages } = props;
|
const { isShowMessages } = props;
|
||||||
const [appGlobalState] = useAtom(globalState);
|
const [appGlobalState] = useAtom(globalState);
|
||||||
const { context, appId, AzureFunctionUrl } = appGlobalState;
|
const { context, appId, AzureFunctionUrl, parentMessageId, chatId, teamsId, channelId } = appGlobalState;
|
||||||
const { textFieldStyles, controlStyles, buttonIconStyles } = useChatGptStyles();
|
const { textFieldStyles, controlStyles, buttonIconStyles } = useChatGptStyles();
|
||||||
const [conversation, setConversation] = React.useState<React.ReactNode[]>([]);
|
const [conversation, setConversation] = React.useState<React.ReactNode[]>([]);
|
||||||
const [textToAsk, setTextToAsk] = React.useState<string>("");
|
const [textToAsk, setTextToAsk] = React.useState<string>("");
|
||||||
|
@ -31,6 +37,12 @@ export const RenderMessages: React.FunctionComponent<IRenderMessagesProps> = (
|
||||||
const { getCompletion } = useChatGpt(context, appId, AzureFunctionUrl);
|
const { getCompletion } = useChatGpt(context, appId, AzureFunctionUrl);
|
||||||
const scrollRef = React.useRef<HTMLDivElement>(null);
|
const scrollRef = React.useRef<HTMLDivElement>(null);
|
||||||
const [error, setError] = React.useState<Error | undefined>(undefined);
|
const [error, setError] = React.useState<Error | undefined>(undefined);
|
||||||
|
const { getChatParentMessage, getChannelParentMessage } = useGraphAPI(context);
|
||||||
|
const executeAutoGetComplete = React.useRef<boolean>(false);
|
||||||
|
const { getTextFromHtml } = useHtmlUtils();
|
||||||
|
|
||||||
|
const hasParentMessage = React.useMemo(() => !!parentMessageId , [parentMessageId]);
|
||||||
|
const isInChannel = React.useMemo(() => !!teamsId && !!channelId, [teamsId, channelId]);
|
||||||
|
|
||||||
const hasError = React.useMemo(() => error !== undefined, [error]);
|
const hasError = React.useMemo(() => error !== undefined, [error]);
|
||||||
|
|
||||||
|
@ -54,8 +66,8 @@ export const RenderMessages: React.FunctionComponent<IRenderMessagesProps> = (
|
||||||
);
|
);
|
||||||
|
|
||||||
const addAnswer = React.useCallback(
|
const addAnswer = React.useCallback(
|
||||||
(answer: string) => {
|
(answer: string, question?: string) => {
|
||||||
const newAnswer = <RenderAnswer answer={answer} key={conversation.length + 1} />;
|
const newAnswer = <RenderAnswer answer={answer} question={question} key={conversation.length + 1} />;
|
||||||
setConversation((prev) => {
|
setConversation((prev) => {
|
||||||
return [...prev, newAnswer];
|
return [...prev, newAnswer];
|
||||||
});
|
});
|
||||||
|
@ -92,6 +104,44 @@ export const RenderMessages: React.FunctionComponent<IRenderMessagesProps> = (
|
||||||
[onSubmit]
|
[onSubmit]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const runAutoGetComplete = React.useCallback(async () => {
|
||||||
|
try {
|
||||||
|
let messageDetails: ChatMessage = undefined;
|
||||||
|
if (!isInChannel ) {
|
||||||
|
messageDetails = await getChatParentMessage(chatId, parentMessageId);
|
||||||
|
} else {
|
||||||
|
messageDetails = await getChannelParentMessage(teamsId, channelId, parentMessageId);
|
||||||
|
}
|
||||||
|
const { body } = messageDetails;
|
||||||
|
if (body) {
|
||||||
|
setError(undefined);
|
||||||
|
const { content, } = body;
|
||||||
|
console.log(body);
|
||||||
|
|
||||||
|
const text = getTextFromHtml(content);
|
||||||
|
if ( text ) {
|
||||||
|
addQuestion(text);
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await getCompletion(text);
|
||||||
|
addAnswer(response, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (hasParentMessage && !executeAutoGetComplete.current) {
|
||||||
|
executeAutoGetComplete.current = true;
|
||||||
|
await runAutoGetComplete();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [hasParentMessage]);
|
||||||
|
|
||||||
if (!isShowMessages) {
|
if (!isShowMessages) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +154,7 @@ export const RenderMessages: React.FunctionComponent<IRenderMessagesProps> = (
|
||||||
|
|
||||||
<Stack tokens={{ padding: 20, childrenGap: 10 }}>
|
<Stack tokens={{ padding: 20, childrenGap: 10 }}>
|
||||||
<Loading isLoading={isLoading} />
|
<Loading isLoading={isLoading} />
|
||||||
<Stack horizontal tokens={{ childrenGap: 5 }} >
|
<Stack horizontal tokens={{ childrenGap: 5 }}>
|
||||||
{hasError ? (
|
{hasError ? (
|
||||||
<ErrorMessage errorMessage={error?.message} showError={hasError} />
|
<ErrorMessage errorMessage={error?.message} showError={hasError} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const SendMessageToChat: React.FunctionComponent<ISendMessageProps> = (
|
||||||
props: React.PropsWithChildren<ISendMessageProps>
|
props: React.PropsWithChildren<ISendMessageProps>
|
||||||
) => {
|
) => {
|
||||||
const [appGlobalState] = useAtom(globalState);
|
const [appGlobalState] = useAtom(globalState);
|
||||||
const { hasTeamsContext, chatId, } = appGlobalState;
|
const { hasTeamsContext, chatId,teamsId,channelId, } = appGlobalState;
|
||||||
const { onSendMessage } = props;
|
const { onSendMessage } = props;
|
||||||
|
|
||||||
const shareIcon: IIconProps = React.useMemo(() => {return { iconName: "Share" }}, []);
|
const shareIcon: IIconProps = React.useMemo(() => {return { iconName: "Share" }}, []);
|
||||||
|
@ -26,8 +26,8 @@ export const SendMessageToChat: React.FunctionComponent<ISendMessageProps> = (
|
||||||
const tooltipId = useId("tooltip");
|
const tooltipId = useId("tooltip");
|
||||||
|
|
||||||
const isInChat = React.useMemo(() => {
|
const isInChat = React.useMemo(() => {
|
||||||
return hasTeamsContext && chatId;
|
return hasTeamsContext && (chatId || teamsId || channelId);
|
||||||
}, [chatId, hasTeamsContext]);
|
}, [chatId,teamsId,channelId, hasTeamsContext]);
|
||||||
|
|
||||||
if (!isInChat) {
|
if (!isInChat) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -19,7 +19,7 @@ const onProcessMarkdownHandler = (md:any, result: { outputHtml: string; didProc
|
||||||
result.didProcess = false;
|
result.didProcess = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const useAdaptiveCardsUtils = function () {
|
export const useAdaptiveCardsUtils = () => {
|
||||||
|
|
||||||
const createAdaptiveCard = React.useCallback((adaptiveCardData, card) => {
|
const createAdaptiveCard = React.useCallback((adaptiveCardData, card) => {
|
||||||
const adaptiveCardToRender = new adaptiveCards.AdaptiveCard();
|
const adaptiveCardToRender = new adaptiveCards.AdaptiveCard();
|
||||||
|
|
|
@ -8,10 +8,18 @@ import {
|
||||||
IHttpClientOptions,
|
IHttpClientOptions,
|
||||||
} from '@microsoft/sp-http';
|
} from '@microsoft/sp-http';
|
||||||
|
|
||||||
/* const APPID = "6b4a20b2-bf2f-4cbb-a162-af960a40c2dc";
|
enum ERole {
|
||||||
const AZURE_FUNCTION_URL = "https://openaifunctionsapp.azurewebsites.net/api/OpenAICompletion"; */
|
user = "user",
|
||||||
|
assistant = "assistant",
|
||||||
|
system = "system",
|
||||||
|
}
|
||||||
|
interface IMessages {
|
||||||
|
role:ERole;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const useChatGpt = (context: BaseComponentContext, appId: string, AzureFunctionUrl: string) => {
|
export const useChatGpt = (context: BaseComponentContext, appId:string, AzureFunctionUrl:string) => {
|
||||||
|
const messages = React.useRef<IMessages[]>([]);
|
||||||
const client = React.useMemo(() => {
|
const client = React.useMemo(() => {
|
||||||
if (context) {
|
if (context) {
|
||||||
return async () => {
|
return async () => {
|
||||||
|
@ -25,27 +33,30 @@ export const useChatGpt = (context: BaseComponentContext, appId: string, AzureFu
|
||||||
const getCompletion = React.useCallback(
|
const getCompletion = React.useCallback(
|
||||||
async (query: string): Promise<string> => {
|
async (query: string): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
|
messages.current.push({ role: ERole.user, content: query });
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
const options: IHttpClientOptions = {
|
const options: IHttpClientOptions = {
|
||||||
headers: { "Content-Type": "application/json;odata=verbose", Accept: "application/json;odata=verbose" },
|
headers: { "Content-Type": "application/json;odata=verbose", Accept: "application/json;odata=verbose" },
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
body: JSON.stringify({ prompt: query }),
|
body: JSON.stringify({ messages: messages.current }),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await (await client()).post(AzureFunctionUrl, AadHttpClient.configurations.v1, options);
|
const response = await (await client()).post(AzureFunctionUrl, AadHttpClient.configurations.v1, options);
|
||||||
const answer = await response.json();
|
const answer = await response.json();
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return answer?.choices[0].text;
|
messages.current.push(answer.choices[0].message)
|
||||||
} else {
|
return answer.choices[0].message.content;
|
||||||
console.log("[getCompletion] error:", answer);
|
} else {
|
||||||
throw new Error("Error on executing the request, please try again later.");
|
console.log("[getCompletion] error:", answer);
|
||||||
}
|
throw new Error("Error on executing the request, please try again later.");
|
||||||
} catch (error) {
|
}
|
||||||
if (!DEBUG) {
|
} catch (error) {
|
||||||
console.log("[getCompletion] error:", error);
|
if (!DEBUG) {
|
||||||
}
|
console.log("[getCompletion] error:", error);
|
||||||
throw error;
|
}
|
||||||
}
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[client]
|
[client]
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,27 +8,52 @@ import {
|
||||||
} from '@microsoft/microsoft-graph-types';
|
} from '@microsoft/microsoft-graph-types';
|
||||||
import { BaseComponentContext } from '@microsoft/sp-component-base';
|
import { BaseComponentContext } from '@microsoft/sp-component-base';
|
||||||
|
|
||||||
export const useGraphAPI = (context: BaseComponentContext) => {
|
export const useGraphAPI = (context: BaseComponentContext) => {
|
||||||
|
const graphClient = React.useMemo(() => {
|
||||||
|
return async () => {
|
||||||
|
const client = await context.msGraphClientFactory.getClient("3");
|
||||||
|
return client;
|
||||||
|
};
|
||||||
|
}, [context]);
|
||||||
|
const sendMessageToChat = React.useCallback(
|
||||||
|
async (chatId: string, chatMessagePayload: object): Promise<ChatMessage> => {
|
||||||
|
const chatMessage = await (await graphClient()).api(`/chats/${chatId}/messages`).post(chatMessagePayload);
|
||||||
|
return chatMessage;
|
||||||
|
},
|
||||||
|
[graphClient]
|
||||||
|
);
|
||||||
|
|
||||||
const sendMessage = React.useCallback(async (chatId: string, message: string):Promise<ChatMessage> => {
|
const sendMessageToChannel = React.useCallback(
|
||||||
const client = await context.msGraphClientFactory.getClient("3");
|
async (teamsId: string, channelId: string, chatMessagePayload: object): Promise<ChatMessage> => {
|
||||||
const response:ChatMessage = await client.api(`/chats/${chatId}/messages`)
|
const channelMessage = await (await graphClient())
|
||||||
.post({
|
.api(`/teams/${teamsId}/channels/${channelId}/messages`)
|
||||||
body: {
|
.post(chatMessagePayload);
|
||||||
content: `${message} (source: ChatGPT)` ,
|
return channelMessage;
|
||||||
},
|
},
|
||||||
});
|
[graphClient]
|
||||||
return response;
|
);
|
||||||
},[context]);
|
|
||||||
|
|
||||||
|
const replyToMessage = React.useCallback( async (teamsId: string, channelId: string, parentMessageId: string, chatMessagePayload: object) => {
|
||||||
|
return (await graphClient())
|
||||||
|
.api(`/teams/${teamsId}/channels/${channelId}/messages/${parentMessageId}/replies`)
|
||||||
|
.post(chatMessagePayload);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const getChatInfo = React.useCallback(async (chatId:string):Promise<Chat> => {
|
const getChatInfo = React.useCallback(
|
||||||
const client = await context.msGraphClientFactory.getClient("3");
|
async (chatId: string): Promise<Chat> => {
|
||||||
const response:Chat = await client.api(`/chats/${chatId}?$expand=members`)
|
const response: Chat = await (await graphClient()).api(`/chats/${chatId}`).get();
|
||||||
.get();
|
return response;
|
||||||
return response;
|
},
|
||||||
},[context]);
|
[context]
|
||||||
|
);
|
||||||
|
|
||||||
return {sendMessage, getChatInfo}
|
const getChatParentMessage = React.useCallback(async (chatId: string, parentMessageId: string):Promise<ChatMessage> => {
|
||||||
|
return (await graphClient()).api(`/chats/${chatId}/messages/${parentMessageId}`).get();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getChannelParentMessage = React.useCallback(async (teamId: string,channelId:string, parentMessageId: string):Promise<ChatMessage> => {
|
||||||
|
return (await graphClient()).api(`/teams/${teamId}/channels/${channelId}/messages/${parentMessageId}`).get();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { sendMessageToChat, sendMessageToChannel, replyToMessage, getChatInfo, getChatParentMessage, getChannelParentMessage };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
interface IHtmlUtils {
|
||||||
|
getTextFromHtml: (html: string) => string;
|
||||||
|
}
|
||||||
|
export const useHtmlUtils = ():IHtmlUtils => {
|
||||||
|
|
||||||
|
const getTextFromHtml = (html: string):string => {
|
||||||
|
const tmp = document.createElement('DIV');
|
||||||
|
tmp.innerHTML = html;
|
||||||
|
return tmp.textContent || tmp.innerText || undefined;
|
||||||
|
};
|
||||||
|
return {getTextFromHtml}
|
||||||
|
};
|
|
@ -4,17 +4,15 @@ import { BaseComponentContext } from '@microsoft/sp-component-base';
|
||||||
import { Guid } from '@microsoft/sp-core-library';
|
import { Guid } from '@microsoft/sp-core-library';
|
||||||
import { isEmpty } from '@microsoft/sp-lodash-subset';
|
import { isEmpty } from '@microsoft/sp-lodash-subset';
|
||||||
|
|
||||||
|
import { useGraphAPI } from '../hooks/useGraphAPI';
|
||||||
import { IAdaptativeCardData } from '../models/IAdaptivecardData';
|
import { IAdaptativeCardData } from '../models/IAdaptivecardData';
|
||||||
import { HostedContents } from '../models/IChatMessage';
|
import { HostedContents } from '../models/IChatMessage';
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
||||||
const graphClient = React.useMemo(() => {
|
|
||||||
return async () => {
|
const { sendMessageToChat, sendMessageToChannel, replyToMessage } = useGraphAPI(context);
|
||||||
const client = await context.msGraphClientFactory.getClient("3");
|
|
||||||
return client;
|
|
||||||
};
|
|
||||||
}, [context]);
|
|
||||||
|
|
||||||
const getHostedContent = React.useCallback(async (adaptiveCard: object, adaptiveCardData: IAdaptativeCardData) => {
|
const getHostedContent = React.useCallback(async (adaptiveCard: object, adaptiveCardData: IAdaptativeCardData) => {
|
||||||
try {
|
try {
|
||||||
|
@ -55,50 +53,17 @@ export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
/* const createChatMembers = React.useCallback((receiverEmail: string) => {
|
|
||||||
try {
|
|
||||||
const currentUser = context.pageContext.user.email;
|
|
||||||
const chatMembers = [
|
|
||||||
{
|
|
||||||
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
||||||
roles: ["owner"],
|
|
||||||
"user@odata.bind": `https://graph.microsoft.com/v1.0/users('${currentUser}')`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
||||||
roles: ["owner"],
|
|
||||||
"user@odata.bind": `https://graph.microsoft.com/v1.0/users('${receiverEmail}')`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return chatMembers;
|
|
||||||
} catch (error) {
|
|
||||||
if (DEBUG) {
|
|
||||||
console.error(`[SendMessage.createChatMembers]: error=${error}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []); */
|
|
||||||
/*
|
|
||||||
const createChat = React.useCallback(
|
|
||||||
async (receiverEmail) => {
|
|
||||||
try {
|
|
||||||
const members = createChatMembers(receiverEmail);
|
|
||||||
const chat = await (await graphClient()).api("/chats").post({ chatType: "oneOnOne", members: members });
|
|
||||||
return chat;
|
|
||||||
} catch (error) {
|
|
||||||
if (DEBUG) {
|
|
||||||
console.error("[SendMessage.createChat]: error=", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[graphClient]
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
const sendMessage = React.useCallback(
|
|
||||||
async (adaptiveCard: object, adaptiveCardData: IAdaptativeCardData, chatId: string) => {
|
|
||||||
try {
|
|
||||||
|
|
||||||
|
const sendMessage = React.useCallback(
|
||||||
|
async (
|
||||||
|
adaptiveCard: object,
|
||||||
|
adaptiveCardData: IAdaptativeCardData,
|
||||||
|
chatId: string,
|
||||||
|
teamsId: string,
|
||||||
|
channelId: string,
|
||||||
|
parentMessageId: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
const { body, attachments, hostedContents } = await getSendMessagePayload(adaptiveCard, adaptiveCardData);
|
const { body, attachments, hostedContents } = await getSendMessagePayload(adaptiveCard, adaptiveCardData);
|
||||||
const chatMessagePayload = {
|
const chatMessagePayload = {
|
||||||
subject: "OpenAI Answer",
|
subject: "OpenAI Answer",
|
||||||
|
@ -106,8 +71,20 @@ export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
||||||
attachments: attachments,
|
attachments: attachments,
|
||||||
hostedContents: hostedContents,
|
hostedContents: hostedContents,
|
||||||
};
|
};
|
||||||
const chatMessage = await (await graphClient()).api(`/chats/${chatId}/messages`).post(chatMessagePayload);
|
if (chatId && !teamsId && !channelId) {
|
||||||
return chatMessage;
|
console.log('channelId', channelId);
|
||||||
|
console.log('teamsId', teamsId);
|
||||||
|
const chatMessage = await sendMessageToChat(chatId, chatMessagePayload, );
|
||||||
|
return chatMessage;
|
||||||
|
}
|
||||||
|
if (teamsId && channelId && !parentMessageId) {
|
||||||
|
const channelMessage = sendMessageToChannel(teamsId, channelId, chatMessagePayload);
|
||||||
|
return channelMessage;
|
||||||
|
}
|
||||||
|
if (teamsId && channelId && parentMessageId) {
|
||||||
|
const replyMessage = await replyToMessage(teamsId, channelId, parentMessageId, chatMessagePayload);
|
||||||
|
return replyMessage;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.error("[SendMessage]: error=", error);
|
console.error("[SendMessage]: error=", error);
|
||||||
|
@ -115,13 +92,20 @@ export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[graphClient]
|
[getSendMessagePayload, sendMessageToChannel, sendMessageToChat, replyToMessage, ]
|
||||||
);
|
);
|
||||||
|
|
||||||
const sendAdativeCardToUsers = React.useCallback(
|
const sendAdativeCardToUsers = React.useCallback(
|
||||||
async (adaptiveCard: object, adaptiveCardData: IAdaptativeCardData, chatId: string) => {
|
async (
|
||||||
|
adaptiveCard: object,
|
||||||
|
adaptiveCardData: IAdaptativeCardData,
|
||||||
|
chatId: string,
|
||||||
|
teamsId: string,
|
||||||
|
channelId: string,
|
||||||
|
parentMessageId: string
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
await sendMessage(adaptiveCard, adaptiveCardData, chatId);
|
await sendMessage(adaptiveCard, adaptiveCardData, chatId, teamsId, channelId, parentMessageId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.error(`[SendMessage.sendAdativeCardToUsers]: error=${error.message}`);
|
console.error(`[SendMessage.sendAdativeCardToUsers]: error=${error.message}`);
|
||||||
|
@ -129,7 +113,7 @@ export const useSendMessageToTeams = (context: BaseComponentContext) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[graphClient]
|
[sendMessage]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { sendAdativeCardToUsers };
|
return { sendAdativeCardToUsers };
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export interface IAdaptativeCardData {
|
export interface IAdaptativeCardData {
|
||||||
date: string;
|
date: string;
|
||||||
answer: string;
|
answer: string;
|
||||||
|
question: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,7 @@ export interface IChatGptProps {
|
||||||
context: BaseComponentContext;
|
context: BaseComponentContext;
|
||||||
theme: ITheme | IReadonlyTheme ;
|
theme: ITheme | IReadonlyTheme ;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
teamsId: string;
|
||||||
|
channelId: string;
|
||||||
|
parentMessageId: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ export interface IGlobalState {
|
||||||
isDarkTheme: boolean;
|
isDarkTheme: boolean;
|
||||||
hasTeamsContext: boolean;
|
hasTeamsContext: boolean;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
teamsId: string;
|
||||||
|
channelId: string;
|
||||||
|
parentMessageId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
AzureFunctionUrl: string;
|
AzureFunctionUrl: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export interface IRenderAnswerProps {
|
export interface IRenderAnswerProps {
|
||||||
answer: string;
|
answer: string;
|
||||||
|
question?:string
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,9 @@ export default class ChatGptWebPart extends BaseClientSideWebPart<IChatGptProps>
|
||||||
theme: this._currentTheme,
|
theme: this._currentTheme,
|
||||||
context: this.context,
|
context: this.context,
|
||||||
chatId: this._chatId,
|
chatId: this._chatId,
|
||||||
|
teamsId: this._teamId,
|
||||||
|
channelId: this._channelId,
|
||||||
|
parentMessageId: this._parentMessageId,
|
||||||
});
|
});
|
||||||
|
|
||||||
ReactDom.render(element, this.domElement);
|
ReactDom.render(element, this.domElement);
|
||||||
|
@ -84,6 +87,7 @@ export default class ChatGptWebPart extends BaseClientSideWebPart<IChatGptProps>
|
||||||
this._channelId = teamsContext.channel?.id;
|
this._channelId = teamsContext.channel?.id;
|
||||||
this._parentMessageId = teamsContext.app.parentMessageId;
|
this._parentMessageId = teamsContext.app.parentMessageId;
|
||||||
|
|
||||||
|
|
||||||
console.log("chatId", this._chatId);
|
console.log("chatId", this._chatId);
|
||||||
console.log("teamId", this._teamId);
|
console.log("teamId", this._teamId);
|
||||||
console.log("channelId", this._channelId);
|
console.log("channelId", this._channelId);
|
||||||
|
|
|
@ -3,7 +3,7 @@ define([], function() {
|
||||||
CgatGPTAppOpenAILabel: " Open AI",
|
CgatGPTAppOpenAILabel: " Open AI",
|
||||||
ChatGPTAppNotificationMessage: "Answer was sent to chat",
|
ChatGPTAppNotificationMessage: "Answer was sent to chat",
|
||||||
ChatGPTAppNotificationTitle: "Sent to chat",
|
ChatGPTAppNotificationTitle: "Sent to chat",
|
||||||
ChatGPTAppPoweredByLabel: "Powered by OpenAI, GPT-3",
|
ChatGPTAppPoweredByLabel: "Powered by OpenAI, ChatGPT-3.5-turbo",
|
||||||
ChatGPTAppPreviewChatInfoMessage: " Please create a mesage first and after return here",
|
ChatGPTAppPreviewChatInfoMessage: " Please create a mesage first and after return here",
|
||||||
PropertyPaneDescription: "ChatGPT App",
|
PropertyPaneDescription: "ChatGPT App",
|
||||||
BasicGroupName: "Properties",
|
BasicGroupName: "Properties",
|
||||||
|
|
Loading…
Reference in New Issue