├── .env ├── .eslintrc.json ├── .github ├── assets │ └── images │ │ ├── aim-custom-function-chatgpt-excel-formulas.png │ │ ├── aim-custom-function-chatgpt-excel-opening-the task-pane.png │ │ ├── aim-custom-function-chatgpt-manifest-validation-check.png │ │ ├── aim-custom-function-chatgpt-run-yo-office.png │ │ └── aim-custom-function-chatgpt-yo-office-add-in-created.png └── workflows │ ├── codeql.yml │ └── webpack.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── assets ├── analytics-in-motion-chatgpt-in-excel-add-in-example.png ├── icon-nlp-128.png ├── icon-nlp-16.png ├── icon-nlp-32.png ├── icon-nlp-64.png ├── icon-nlp-80.png ├── logo-aim-filled.png ├── logo-filled.png └── logo.svg ├── babel.config.json ├── dist ├── 768cb1c2bbf0fa2c9e2d.css ├── a698c8dfdb3534564726.svg ├── assets │ ├── analytics-in-motion-chatgpt-in-excel-add-in-example.png │ ├── icon-nlp-128.png │ ├── icon-nlp-16.png │ ├── icon-nlp-32.png │ ├── icon-nlp-64.png │ ├── icon-nlp-80.png │ ├── logo-aim-filled.png │ ├── logo-filled.png │ └── logo.svg ├── commands.js ├── commands.js.map ├── functions.js ├── functions.js.map ├── functions.json ├── manifest.xml ├── polyfill.js ├── polyfill.js.map ├── taskpane.html ├── taskpane.js ├── taskpane.js.LICENSE.txt └── taskpane.js.map ├── favicon.ico ├── manifest.xml ├── package-lock.json ├── package.json ├── src ├── commands │ └── commands.js ├── functions │ └── functions.js └── taskpane │ ├── taskpane.css │ ├── taskpane.html │ └── taskpane.js ├── tsconfig.json └── webpack.config.js /.env: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=sk-XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "office-addins" 4 | ], 5 | "extends": [ 6 | "plugin:office-addins/recommended" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/assets/images/aim-custom-function-chatgpt-excel-formulas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/.github/assets/images/aim-custom-function-chatgpt-excel-formulas.png -------------------------------------------------------------------------------- /.github/assets/images/aim-custom-function-chatgpt-excel-opening-the task-pane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/.github/assets/images/aim-custom-function-chatgpt-excel-opening-the task-pane.png -------------------------------------------------------------------------------- /.github/assets/images/aim-custom-function-chatgpt-manifest-validation-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/.github/assets/images/aim-custom-function-chatgpt-manifest-validation-check.png -------------------------------------------------------------------------------- /.github/assets/images/aim-custom-function-chatgpt-run-yo-office.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/.github/assets/images/aim-custom-function-chatgpt-run-yo-office.png -------------------------------------------------------------------------------- /.github/assets/images/aim-custom-function-chatgpt-yo-office-add-in-created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/.github/assets/images/aim-custom-function-chatgpt-yo-office-add-in-created.png -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '34 4 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Use only 'java' to analyze code written in Java, Kotlin or both 38 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 39 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v3 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v2 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | 54 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 55 | # queries: security-extended,security-and-quality 56 | 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v2 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 65 | 66 | # If the Autobuild fails above, remove it and uncomment the following three lines. 67 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 68 | 69 | # - run: | 70 | # echo "Run, Build Application using script" 71 | # ./location_of_script_within_repo/buildscript.sh 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v2 75 | with: 76 | category: "/language:${{matrix.language}}" 77 | -------------------------------------------------------------------------------- /.github/workflows/webpack.yml: -------------------------------------------------------------------------------- 1 | name: NodeJS with Webpack 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x, 18.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Build 26 | run: | 27 | npm install 28 | npx webpack 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Environments 4 | .env 5 | .venv 6 | env/ 7 | venv/ 8 | ENV/ 9 | env.bak/ 10 | venv.bak/ 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "ms-edgedevtools.vscode-edge-devtools", 8 | "msoffice.microsoft-office-add-in-debugger", 9 | "dbaeumer.vscode-eslint", 10 | "esbenp.prettier-vscode" 11 | ], 12 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 13 | "unwantedRecommendations": [] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Excel Desktop (Edge Chromium)", 9 | "type": "msedge", 10 | "request": "attach", 11 | "port": 9229, 12 | "timeout": 600000, 13 | "webRoot": "${workspaceRoot}", 14 | "preLaunchTask": "Debug: Excel Desktop", 15 | "postDebugTask": "Stop Debug" 16 | }, 17 | { 18 | "name": "Excel Desktop (Edge Legacy)", 19 | "type": "office-addin", 20 | "request": "attach", 21 | "url": "https://localhost:3000/taskpane.html?_host_Info=Excel$Win32$16.01$en-US$$$$0", 22 | "port": 9222, 23 | "timeout": 600000, 24 | "webRoot": "${workspaceRoot}", 25 | "preLaunchTask": "Debug: Excel Desktop", 26 | "postDebugTask": "Stop Debug" 27 | }, 28 | { 29 | "name": "Office Online (Chrome)", 30 | "type": "chrome", 31 | "request": "launch", 32 | // To debug your Add-in: 33 | // 1. When prompted, enter the url (share link) to an Office Online document. 34 | // 2. Sideload your Add-in. https://docs.microsoft.com/en-us/office/dev/add-ins/testing/sideload-office-add-ins-for-testing 35 | "url": "${input:officeOnlineDocumentUrl}", 36 | "webRoot": "${workspaceFolder}", 37 | "preLaunchTask": "Debug: Web" 38 | }, 39 | { 40 | "name": "Office Online (Edge Chromium)", 41 | "type": "msedge", 42 | "request": "launch", 43 | "port": 9222, 44 | // To debug your Add-in: 45 | // 1. When prompted, enter the url (share link) to an Office Online document. 46 | // 2. Sideload your Add-in. https://docs.microsoft.com/en-us/office/dev/add-ins/testing/sideload-office-add-ins-for-testing 47 | "url": "${input:officeOnlineDocumentUrl}", 48 | "webRoot": "${workspaceFolder}", 49 | "preLaunchTask": "Debug: Web" 50 | } 51 | ], 52 | "inputs": [ 53 | { 54 | "id": "officeOnlineDocumentUrl", 55 | "type": "promptString", 56 | "description": "Please enter the url for the Office Online document." 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | "typescript" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build (Development)", 8 | "type": "npm", 9 | "script": "build:dev", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "presentation": { 15 | "clear": true, 16 | "panel": "shared", 17 | "showReuseMessage": false 18 | } 19 | }, 20 | { 21 | "label": "Build (Production)", 22 | "type": "npm", 23 | "script": "build", 24 | "group": "build", 25 | "presentation": { 26 | "clear": true, 27 | "panel": "shared", 28 | "showReuseMessage": false 29 | } 30 | }, 31 | { 32 | "label": "Debug: Excel Desktop", 33 | "type": "npm", 34 | "script": "start:desktop -- --app excel", 35 | "presentation": { 36 | "clear": true, 37 | "panel": "dedicated", 38 | }, 39 | "problemMatcher": [] 40 | }, 41 | { 42 | "label": "Debug: Web", 43 | "type": "npm", 44 | "script": "start:web", 45 | "presentation": { 46 | "clear": true, 47 | "panel": "shared", 48 | "showReuseMessage": false 49 | }, 50 | "problemMatcher": [] 51 | }, 52 | { 53 | "label": "Dev Server", 54 | "type": "npm", 55 | "script": "dev-server", 56 | "presentation": { 57 | "clear": true, 58 | "panel": "dedicated" 59 | }, 60 | "problemMatcher": [] 61 | }, 62 | { 63 | "label": "Install", 64 | "type": "npm", 65 | "script": "install", 66 | "presentation": { 67 | "clear": true, 68 | "panel": "shared", 69 | "showReuseMessage": false 70 | }, 71 | "problemMatcher": [] 72 | }, 73 | { 74 | "label": "Lint: Check for problems", 75 | "type": "npm", 76 | "script": "lint", 77 | "problemMatcher": [ 78 | "$eslint-stylish" 79 | ] 80 | }, 81 | { 82 | "label": "Lint: Fix all auto-fixable problems", 83 | "type": "npm", 84 | "script": "lint:fix", 85 | "problemMatcher": [ 86 | "$eslint-stylish" 87 | ] 88 | }, 89 | { 90 | "label": "Stop Debug", 91 | "type": "npm", 92 | "script": "stop", 93 | "presentation": { 94 | "clear": true, 95 | "panel": "shared", 96 | "showReuseMessage": false 97 | }, 98 | "problemMatcher": [] 99 | }, 100 | { 101 | "label": "Watch", 102 | "type": "npm", 103 | "script": "watch", 104 | "presentation": { 105 | "clear": true, 106 | "panel": "dedicated" 107 | }, 108 | "problemMatcher": [] 109 | }, 110 | ] 111 | } 112 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | pi@analyticsinmotion.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Analytics in Motion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sentiment Analysis in Excel using ChatGPT 2 | Discover awesome ChatGPT features you can use in Excel 3 |

4 | 5 | 6 | ![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?logo=javascript&logoColor=%23F7DF1E)   7 | [![Node.js](https://img.shields.io/badge/Node.js-43853D?logo=node.js&logoColor=white)](https://nodejs.org/en/)   8 | ![NPM](https://img.shields.io/badge/NPM-%23CB3837.svg?logo=npm&logoColor=white)   9 | ![Webpack](https://img.shields.io/badge/webpack-%238DD6F9.svg?logo=webpack&logoColor=black)   10 | ![Microsoft Excel](https://img.shields.io/badge/Microsoft_Excel-217346?logo=microsoft-excel&logoColor=white)   11 | [![ChatGPT](https://img.shields.io/badge/ChatGPT-74aa9c?style=badge&logo=openai&logoColor=white)](https://chat.openai.com)   12 | [![MIT license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/blob/main/LICENSE.md)   13 | ![Lifecycle:Stable](https://img.shields.io/badge/Lifecycle-Stable-97ca00)   14 | [![NodeJS with Webpack](https://github.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/actions/workflows/webpack.yml/badge.svg)](https://github.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/actions/workflows/webpack.yml)   15 | [![CodeQL](https://github.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/actions/workflows/codeql.yml/badge.svg)](https://github.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/actions/workflows/codeql.yml)   16 | [![OpenAI](https://img.shields.io/badge/OpenAI-000000?style=badge&logo=openai&logoColor=white)](https://openai.com)   17 | [![Analytics in Motion](https://raw.githubusercontent.com/analyticsinmotion/.github/main/assets/images/analytics-in-motion-github-badge-rounded.svg)](https://www.analyticsinmotion.com)   18 | 19 | 20 | 21 | ## 1. Description 22 | 23 | With an Excel Custom Function Add-in you can make direct calls to ChatGPT for NLP capabilities such as text Moderation and Sentiment Analysis. Results from ChatGPT will be returned to your worksheet almost instantly. 24 |

25 | 26 | 27 | 28 | https://user-images.githubusercontent.com/52817125/221394461-92dd3f83-5d41-4f32-9f48-3d701f523b2e.mp4 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | ## 2. Getting Started 37 | ### 2.1 Dependencies 38 | - Requires an OpenAI API Key (create an account and get API Key at https://chat.openai.com) 39 | - Requires Microsoft Excel (https://www.microsoft.com/en-us) 40 | - Requires Node.js (>= 18.14.2 LTS) (https://nodejs.org/en/) 41 | - Requires NPM (>= 9.5.0) (https://www.npmjs.com) 42 | - Requires dotenv-webpack (https://www.npmjs.com/package/dotenv-webpack) 43 | - Requires Yo Office generator (https://github.com/OfficeDev/generator-office) 44 | 45 | Please be aware of the [costs](https://openai.com/pricing) associated with using the OpenAI API when utilizing this project. 46 | 47 | ### 2.2 Editing, Debugging and Testing 48 | - Visual Studio Code (https://code.visualstudio.com/) 49 | - NPM Intellisense Extension (RECOMMENDED) 50 | - Microsoft Edge Tools for VS Code Extension (RECOMMENDED) 51 | 52 | ### 2.3 Directory Structure 53 | This repository contains the architecture and source code used by the Yeoman generator for Office Add-ins (Yo Office generator). The generator creates the scaffolding of files for a variety of Office Add-in projects. Listed below are the main directories/files that were added or edited for this project. 54 | 55 | 56 | . 57 | ├── assets 58 | ├── src 59 | │ ├── functions 60 | │ │ └── functions.js 61 | │ └── taskpane 62 | │ │ ├── taskpane.css 63 | │ │ └── taskpane.html 64 | ├── .env 65 | ├── manifest.xml 66 | ├── tsconfig.json 67 | └── webpack.config.js 68 | 69 | 70 |
71 | 72 | 73 | ## 3. Instructions 74 | This Excel Custom Function provides two formulas that can be applied to text data - *Moderation* and *Sentiment*.
75 |
76 | To utilize these formulas simply type ```=chatgpt``` in the formula bar and the predictive formula dropdown should appear. You can either continue to type the formula or select one of the dropdown options. 77 |
78 | 79 | 80 | 81 | 82 | 83 | ### 3.1 MODERATION Check Formula 84 | 85 | Valid Moderation Input include: 86 | 87 | Direct Cell References 88 | ``` 89 | =CHATGPT.MODERATION(A2) 90 | ``` 91 | Entering text directly. Please ensure any text is enclosed in double quotation marks. 92 | ``` 93 | =CHATGPT.MODERATION("Any text you want to be checked") 94 | ``` 95 | Valid Moderation Responses include: 96 | - Passed 97 | - Failed 98 | - Blank 99 | 100 | 101 | 102 | ### 3.2 SENTIMENT Classifier Formula 103 | 104 | Valid Sentiment Input include: 105 | ``` 106 | =CHATGPT.SENTIMENT(A2) 107 | 108 | =CHATGPT.SENTIMENT("Any text you want to find the sentiment for") 109 | ``` 110 | Valid Sentiment Responses include: 111 | - Positive 112 | - Negative 113 | - Neutral 114 | - Blank 115 | 116 |
117 | 118 | To apply the Sentiment Classifier only on text that has passed moderation you can apply a formula similar to this: 119 | ``` 120 | =IF(B2="Failed","",CHATGPT.SENTIMENT(A2)) 121 | ``` 122 | 123 | 124 | ### 3.3 Open Task Pane 125 | 126 | To open the task pane and read more information about this custom function: 127 | - Click the ribbon tab **AI Copilot** 128 | - Select **NLP Functions** 129 | 130 | The taks pane will open in the right-hand side of the workbook with the title **NLP Functions with ChatGPT** 131 |

132 | 133 | 134 | 135 |
136 | 137 | 138 | ## 4. Installation 139 | ### 4.1 Install Node.js 140 | - Download and install Node.js 141 | 142 | ### 4.2 Install YO OFFICE - Yeoman generator for Office Add-ins 143 | - Install yo generator office at the Command Line. 144 | 145 | Please ensure the command prompt has been opened with administrative priviledges 146 | 147 | ``` npm install -g yo generator-office``` 148 | 149 | ### 4.3 Run YO OFFICE 150 | - Change directory to the location where the project will be built 151 | ``` 152 | cd C:\path where project will be built 153 | ``` 154 | 155 | - Run yo office 156 | ``` 157 | yo office 158 | ``` 159 | 160 | 161 | - Enter the following settings: 162 | - **Choose a project type:** `Excel Custom Functions using a Shared Runtime` 163 | - **Choose a script type:** `JavaScript` 164 | - **What do you want to name your add-in?** `ChatGPT` 165 | 166 | The Yeoman generator will create the project files and install supporting Node components. 167 | 168 | 169 | 170 | - Navigate to the root folder of the project (which is the name of your add-in) 171 | ``` 172 | cd ChatGPT 173 | ``` 174 | 175 | - Build the project 176 | ``` 177 | npm start 178 | ``` 179 | 180 | ### 4.4 Install dotenv-webpack 181 | Enter the following command to instal dotenv-webpack 182 | ``` 183 | npm install dotenv-webpack --save-dev 184 | ``` 185 | For further information about installing and configuring dotenv-webpack please read the following: https://github.com/mrsteele/dotenv-webpack/blob/master/README.md 186 |
187 | 188 | 189 | ### 4.5 Add .env file into your project 190 | - Copy the .env file from this repository and add it into the project root folder 191 | - Open the .env file (in VS Code or any text editor) and replace the dummy API Key with your OpenAI API Key. 192 | 193 | 194 | ### 4.6 Open the manifest.xml file in your project 195 | - Copy the ID tag. This is a unique identifier for each manifest file and should not be the same for any project 196 | ``` 197 | 5qr6d5g8-76yt-4b8z-97jh-83577524e6e1 198 | ``` 199 | - Download the manifest.xml file from this repository. Replace the ID tag in that file with the one copied from your project. 200 | 201 | ### 4.7 Download the remaining directories/files in this repostory 202 | - Please Note: This repository contains almost all the files from the Yeoman generator. Many of these files you will not need to edit and can be left alone. The files that willl need to be replaced or edited are specified in the directory structure in Section 2.3 above. 203 | 204 | 205 | ### 4.8 Validate the manifest.xml file 206 | - To ensure that your XML file is correct and complete after editing please run the Office Add-in Validator. This will allow you to identify and fix any potential issues with the manifest.xml file 207 | ``` 208 | npm run validate 209 | ``` 210 | 211 | 212 | 213 |
214 | 215 | 216 | ## 5. OpenAI API Rate Limits 217 | 218 | A rate limit is a restriction that an API imposes on the number of times a user or client can access the server within a specified period of time. 219 | Rate limits are a common practice for APIs, and they're put in place to help protect against abuse or misuse, help manage the aggregate load on infrastructure, and ensure that everyone has fair access. 220 | 221 | To see the latest API rate limits per user tier please read the following: https://platform.openai.com/docs/guides/rate-limits/overview 222 | 223 | Each cell in Excel where the CHATGPT.SENTIMENT formula is executed should be considered and counted as a single Request. 224 | 225 |
226 | 227 | 228 | ## 6. Best Practices for API Key Safety 229 | 230 | Your OpenAI APIKEY key/s should be kept secure and private at all times. 231 | 232 | Please follow the best practices guide for API security from OpenAI 233 |
234 | https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png -------------------------------------------------------------------------------- /assets/icon-nlp-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/icon-nlp-128.png -------------------------------------------------------------------------------- /assets/icon-nlp-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/icon-nlp-16.png -------------------------------------------------------------------------------- /assets/icon-nlp-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/icon-nlp-32.png -------------------------------------------------------------------------------- /assets/icon-nlp-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/icon-nlp-64.png -------------------------------------------------------------------------------- /assets/icon-nlp-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/icon-nlp-80.png -------------------------------------------------------------------------------- /assets/logo-aim-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/logo-aim-filled.png -------------------------------------------------------------------------------- /assets/logo-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/assets/logo-filled.png -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 226 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /dist/768cb1c2bbf0fa2c9e2d.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | html, 7 | body { 8 | width: 100%; 9 | height: 100%; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | ul { 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | .ms-welcome__header { 20 | padding: 10px; 21 | padding-bottom: 20px; 22 | padding-top: 35px; 23 | display: -webkit-flex; 24 | display: flex; 25 | -webkit-flex-direction: column; 26 | flex-direction: column; 27 | align-items: center; 28 | } 29 | 30 | .ms-welcome__main { 31 | display: -webkit-flex; 32 | display: flex; 33 | -webkit-flex-direction: column; 34 | flex-direction: column; 35 | -webkit-flex-wrap: nowrap; 36 | flex-wrap: nowrap; 37 | -webkit-align-items: center; 38 | align-items: center; 39 | -webkit-flex: 1 0 0; 40 | flex: 1 0 0; 41 | padding: 10px 20px; 42 | } 43 | 44 | .ms-welcome__main > h2 { 45 | width: 100%; 46 | text-align: center; 47 | } 48 | 49 | .ms-welcome__features { 50 | list-style-type: none; 51 | margin-top: 20px; 52 | } 53 | 54 | .ms-welcome__features.ms-List .ms-ListItem { 55 | padding-bottom: 20px; 56 | display: -webkit-flex; 57 | display: flex; 58 | } 59 | 60 | .ms-welcome__features.ms-List .ms-ListItem > .ms-Icon { 61 | margin-right: 10px; 62 | } 63 | 64 | .ms-welcome__action.ms-Button--hero { 65 | margin-top: 30px; 66 | } 67 | 68 | .ms-Button.ms-Button--hero .ms-Button-label { 69 | color: #0078d7; 70 | } 71 | 72 | .ms-Button.ms-Button--hero:hover .ms-Button-label, 73 | .ms-Button.ms-Button--hero:focus .ms-Button-label{ 74 | color: #005a9e; 75 | cursor: pointer; 76 | } 77 | 78 | b { 79 | font-weight: bold; 80 | } 81 | 82 | .ms-font-su { 83 | font-size: 32px; 84 | } 85 | 86 | code { 87 | font-family: monospace; 88 | font-size: inherit; 89 | background: #ffeff0; 90 | word-wrap: break-word; 91 | padding: .1rem .3rem .2rem; 92 | border-radius: .2rem; 93 | } 94 | 95 | .footer{ 96 | background:#2a2a2a; 97 | padding:10px 0px; 98 | font-family: 'Play', sans-serif; 99 | text-align:center; 100 | } 101 | 102 | .footer .row{ 103 | width:100%; 104 | margin:1% 0%; 105 | padding:0.6% 0%; 106 | color:gray; 107 | font-size:0.8em; 108 | } 109 | 110 | .footer .row a{ 111 | text-decoration:none; 112 | color:gray; 113 | transition:0.5s; 114 | } 115 | 116 | .footer .row a:hover{ 117 | color:#fff; 118 | } 119 | 120 | .footer .row ul{ 121 | width:100%; 122 | } 123 | 124 | .footer .row a i{ 125 | font-size:2em; 126 | margin:0% 5%; 127 | } 128 | -------------------------------------------------------------------------------- /dist/a698c8dfdb3534564726.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 226 | -------------------------------------------------------------------------------- /dist/assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png -------------------------------------------------------------------------------- /dist/assets/icon-nlp-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/icon-nlp-128.png -------------------------------------------------------------------------------- /dist/assets/icon-nlp-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/icon-nlp-16.png -------------------------------------------------------------------------------- /dist/assets/icon-nlp-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/icon-nlp-32.png -------------------------------------------------------------------------------- /dist/assets/icon-nlp-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/icon-nlp-64.png -------------------------------------------------------------------------------- /dist/assets/icon-nlp-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/icon-nlp-80.png -------------------------------------------------------------------------------- /dist/assets/logo-aim-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/logo-aim-filled.png -------------------------------------------------------------------------------- /dist/assets/logo-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/dist/assets/logo-filled.png -------------------------------------------------------------------------------- /dist/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 226 | -------------------------------------------------------------------------------- /dist/commands.js: -------------------------------------------------------------------------------- 1 | !function(){var e={};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),Office.onReady((function(){})),("undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==e.g?e.g:void 0).action=function(e){var n={type:Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,message:"Performed action.",icon:"Icon.80x80",persistent:!0};Office.context.mailbox.item.notificationMessages.replaceAsync("action",n),e.completed()}}(); 2 | //# sourceMappingURL=commands.js.map -------------------------------------------------------------------------------- /dist/commands.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"commands.js","mappings":"YACA,IAAIA,EAAsB,CAAC,ECD3BA,EAAoBC,EAAI,WACvB,GAA0B,iBAAfC,WAAyB,OAAOA,WAC3C,IACC,OAAOC,MAAQ,IAAIC,SAAS,cAAb,EAGhB,CAFE,MAAOC,GACR,GAAsB,iBAAXC,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCOxBC,OAAOC,SAAQ,WACb,KAuBuB,oBAATC,KACVA,KACkB,oBAAXH,OACPA,YACkB,IAAXI,EAAAA,EACPA,EAAAA,OACAC,GAMJC,OA5BF,SAAgBC,GACd,IAAMC,EAAU,CACdC,KAAMR,OAAOS,aAAaC,4BAA4BC,qBACtDJ,QAAS,oBACTK,KAAM,aACNC,YAAY,GAIdb,OAAOc,QAAQC,QAAQC,KAAKC,qBAAqBC,aAAa,SAAUX,GAGxED,EAAMa,WACR,C","sources":["webpack://excel-custom-functions-js/webpack/bootstrap","webpack://excel-custom-functions-js/webpack/runtime/global","webpack://excel-custom-functions-js/./src/commands/commands.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","/*\n * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n * See LICENSE in the project root for license information.\n */\n\n/* global global, Office, self, window */\n\nOffice.onReady(() => {\n // If needed, Office.js is ready to be called\n});\n\n/**\n * Shows a notification when the add-in command is executed.\n * @param event\n */\nfunction action(event) {\n const message = {\n type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,\n message: \"Performed action.\",\n icon: \"Icon.80x80\",\n persistent: true,\n };\n\n // Show a notification message\n Office.context.mailbox.item.notificationMessages.replaceAsync(\"action\", message);\n\n // Be sure to indicate when the add-in command function is complete\n event.completed();\n}\n\nfunction getGlobal() {\n return typeof self !== \"undefined\"\n ? self\n : typeof window !== \"undefined\"\n ? window\n : typeof global !== \"undefined\"\n ? global\n : undefined;\n}\n\nconst g = getGlobal();\n\n// the add-in command functions need to be available in global scope\ng.action = action;\n"],"names":["__webpack_require__","g","globalThis","this","Function","e","window","Office","onReady","self","global","undefined","action","event","message","type","MailboxEnums","ItemNotificationMessageType","InformationalMessage","icon","persistent","context","mailbox","item","notificationMessages","replaceAsync","completed"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/functions.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";CustomFunctions.associate("MODERATION",(function(e){return new Promise((function(t,n){e||t("Blank"),fetch("https://api.openai.com/v1/moderations",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer sk-XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx"},body:JSON.stringify({model:"text-moderation-latest",input:e})}).then((function(e){return e.json()})).then((function(e){var n=JSON.stringify(e.results[0].flagged);t("false"===n?"Passed":"true"===n?"Failed":"Error")}))}))})),CustomFunctions.associate("SENTIMENT",(function(e){return new Promise((function(t,n){e||t("Blank"),fetch("https://api.openai.com/v1/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer sk-XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx"},body:JSON.stringify({model:"text-curie-001",prompt:"Decide whether a Tweet's sentiment is positive, neutral, or negative. Tweet:"+e+"Sentiment: ",max_tokens:20,temperature:0,top_p:1,frequency_penalty:.5,presence_penalty:0,n:1})}).then((function(e){return e.json()})).then((function(e){var n=JSON.stringify(e.choices[0].text).replace(/\\n/g,"").replace(/['"]+/g,""),o=n.toLowerCase().charAt(0).toUpperCase()+n.slice(1);t(o)}))}))}))}(); 2 | //# sourceMappingURL=functions.js.map -------------------------------------------------------------------------------- /dist/functions.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"functions.js","mappings":"yBAiGAA,gBAAgBC,UAAU,cAzFnB,SAAoBC,GAKzB,OAAO,IAAIC,SAAQ,SAAUC,EAASC,GAC/BH,GACHE,EAAQ,SAEZE,MARY,wCAQD,CACTC,OAPa,OAQbC,QAAS,CACL,eAAgB,mBAChB,cAAiB,8DAErBC,KAAMC,KAAKC,UAAU,CACjB,MAZQ,yBAaR,MAAST,MAGhBU,MAAK,SAAUC,GACd,OAAOA,EAASC,MAChB,IAEDF,MAAK,SAAUE,GACd,IAAMD,EAAWH,KAAKC,UAAUG,EAAKC,QAAQ,GAAGC,SAYhDZ,EATiB,UAAbS,EACO,SAEa,SAAbA,EACA,SAGA,QAGb,GACA,GACA,IAkDAb,gBAAgBC,UAAU,aAzCnB,SAAmBC,GAOxB,OAAO,IAAIC,SAAQ,SAAUC,EAASC,GAC/BH,GACHE,EAAQ,SAEZE,MAVY,wCAUD,CACTC,OATa,OAUbC,QAAS,CACL,eAAgB,mBAChB,cAAiB,8DAErBC,KAAMC,KAAKC,UAAU,CACjB,MAdQ,iBAeR,OAdS,+EAcUT,EAbV,cAcT,WAAc,GACd,YAAe,EACf,MAAS,EACT,kBAAqB,GACrB,iBAAoB,EACpB,EAAK,MAGZU,MAAK,SAAUC,GACd,OAAOA,EAASC,MAChB,IAEDF,MAAK,SAAUE,GACd,IACIG,EADaP,KAAKC,UAAUG,EAAKI,QAAQ,GAAGhB,MACvBiB,QAAQ,OAAQ,IAAIA,QAAQ,SAAU,IAC3DC,EAAYH,EAAUI,cAAcC,OAAO,GAAGC,cAAgBN,EAAUO,MAAM,GAClFpB,EAAQgB,EACV,GACA,GACA,G","sources":["webpack://excel-custom-functions-js/./src/functions/functions.js"],"sourcesContent":["/* global clearInterval, console, setInterval */\n\n/**\n * Content Moderation using ChatGPT\n * @customfunction\n * @param {string} text The text to be checked\n * @returns {string} Result whether the modertaion has passed or failed.\n */\nexport function moderation(text) {\n const url = \"https://api.openai.com/v1/moderations\";\n const apikey = process.env.OPENAI_API_KEY;\n const method = \"POST\";\n const model = \"text-moderation-latest\";\n return new Promise(function (resolve, reject) {\n if (!text) {\n resolve(\"Blank\");\n }\n fetch(url, {\n method: method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer ' + apikey\n },\n body: JSON.stringify({\n 'model': model,\n 'input': text\n })\n})\n.then(function (response){\n return response.json();\n }\n)\n.then(function (json) {\n const response = JSON.stringify(json.results[0].flagged);\n let result = \"\";\n let cellcolor = \"\";\n if (response === \"false\") {\n result = \"Passed\";\n cellcolor = \"green\";\n } else if (response === \"true\") {\n result = \"Failed\";\n cellcolor = \"red\";\n } else {\n result = \"Error\";\n }\n resolve(result);\n})\n})\n}\n\n\n/**\n * Sentiment Analysis using ChatGPT\n * @customfunction\n * @param {string} text The text to be analyzed\n * @returns {string} Result whether the text is Positive, Negative or Neutral.\n */\nexport function sentiment(text) {\n const url = \"https://api.openai.com/v1/completions\";\n const apikey = process.env.OPENAI_API_KEY;\n const method = \"POST\";\n const model = \"text-curie-001\";\n const prefix = \"Decide whether a Tweet's sentiment is positive, neutral, or negative. Tweet:\";\n const suffix = \"Sentiment: \";\n return new Promise(function (resolve, reject) {\n if (!text) {\n resolve(\"Blank\");\n }\n fetch(url, {\n method: method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer ' + apikey\n },\n body: JSON.stringify({\n 'model': model,\n 'prompt': prefix + text + suffix,\n 'max_tokens': 20,\n 'temperature': 0,\n 'top_p': 1,\n 'frequency_penalty': 0.5,\n 'presence_penalty': 0,\n 'n': 1\n })\n})\n.then(function (response){\n return response.json();\n }\n)\n.then(function (json) {\n const response = JSON.stringify(json.choices[0].text);\n let response2 = response.replace(/\\\\n/g, '').replace(/['\"]+/g, '');\n let response3 = response2.toLowerCase().charAt(0).toUpperCase() + response2.slice(1);\n resolve(response3);\n})\n})\n}\nCustomFunctions.associate(\"MODERATION\", moderation);\nCustomFunctions.associate(\"SENTIMENT\", sentiment);"],"names":["CustomFunctions","associate","text","Promise","resolve","reject","fetch","method","headers","body","JSON","stringify","then","response","json","results","flagged","response2","choices","replace","response3","toLowerCase","charAt","toUpperCase","slice"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/functions.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": [ 3 | { 4 | "description": "Content Moderation using ChatGPT", 5 | "id": "MODERATION", 6 | "name": "MODERATION", 7 | "parameters": [ 8 | { 9 | "description": "The text to be checked", 10 | "name": "text", 11 | "type": "string" 12 | } 13 | ], 14 | "result": { 15 | "type": "string" 16 | } 17 | }, 18 | { 19 | "description": "Sentiment Analysis using ChatGPT", 20 | "id": "SENTIMENT", 21 | "name": "SENTIMENT", 22 | "parameters": [ 23 | { 24 | "description": "The text to be analyzed", 25 | "name": "text", 26 | "type": "string" 27 | } 28 | ], 29 | "result": { 30 | "type": "string" 31 | } 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /dist/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1546c5f2-73ca-4e9e-9736-9803319e6c89 4 | 1.0.0.0 5 | Analytics in Motion 6 | en-US 7 | 8 | 9 | 10 | 11 | 12 | 13 | https://www.analyticsinmotion.com 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ReadWriteDocument 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | <Description resid="GetStarted.Description"/> 51 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 52 | </GetStarted> 53 | <FunctionFile resid="Taskpane.Url"/> 54 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 55 | <!--<OfficeTab id="TabHome">--> 56 | <CustomTab id="AICopilot.Tab"> 57 | <Group id="AICopilot.Tab.Group1"> 58 | <Label resid="AICopilot.Tab.Group1.Label"/> 59 | <Icon> 60 | <bt:Image size="16" resid="Icon.16x16"/> 61 | <bt:Image size="32" resid="Icon.32x32"/> 62 | <bt:Image size="80" resid="Icon.80x80"/> 63 | </Icon> 64 | <Control xsi:type="Button" id="TaskpaneButton"> 65 | <Label resid="TaskpaneButton.Label"/> 66 | <Supertip> 67 | <Title resid="TaskpaneButton.Label"/> 68 | <Description resid="TaskpaneButton.Tooltip"/> 69 | </Supertip> 70 | <Icon> 71 | <bt:Image size="16" resid="Icon.16x16"/> 72 | <bt:Image size="32" resid="Icon.32x32"/> 73 | <bt:Image size="80" resid="Icon.80x80"/> 74 | </Icon> 75 | <Action xsi:type="ShowTaskpane"> 76 | <TaskpaneId>ButtonId1</TaskpaneId> 77 | <SourceLocation resid="Taskpane.Url"/> 78 | </Action> 79 | </Control> 80 | </Group> 81 | 82 | <Label resid="AICopilot.Tab.Label"/> 83 | 84 | </CustomTab> 85 | <!--</OfficeTab>--> 86 | </ExtensionPoint> 87 | </DesktopFormFactor> 88 | </Host> 89 | </Hosts> 90 | <Resources> 91 | <bt:Images> 92 | <bt:Image id="Icon.16x16" DefaultValue="https://www.analyticsinmotion.com/assets/icon-nlp-16.png"/> 93 | <bt:Image id="Icon.32x32" DefaultValue="https://www.analyticsinmotion.com/assets/icon-nlp-32.png"/> 94 | <bt:Image id="Icon.80x80" DefaultValue="https://www.analyticsinmotion.com/assets/icon-nlp-80.png"/> 95 | </bt:Images> 96 | <bt:Urls> 97 | <bt:Url id="Functions.Script.Url" DefaultValue="https://www.analyticsinmotion.com/functions.js"/> 98 | <bt:Url id="Functions.Metadata.Url" DefaultValue="https://www.analyticsinmotion.com/functions.json"/> 99 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/> 100 | <bt:Url id="Taskpane.Url" DefaultValue="https://www.analyticsinmotion.com/taskpane.html"/> 101 | </bt:Urls> 102 | <bt:ShortStrings> 103 | <bt:String id="Functions.Namespace" DefaultValue="CHATGPT"/> 104 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/> 105 | <bt:String id="AICopilot.Tab.Group1.Label" DefaultValue="ChatGPT"/> 106 | <bt:String id="TaskpaneButton.Label" DefaultValue="NLP Functions"/> 107 | <bt:String id="AICopilot.Tab.Label" DefaultValue="AI Copilot"/> 108 | </bt:ShortStrings> 109 | <bt:LongStrings> 110 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started."/> 111 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/> 112 | </bt:LongStrings> 113 | </Resources> 114 | </VersionOverrides> 115 | </OfficeApp> -------------------------------------------------------------------------------- /dist/taskpane.html: -------------------------------------------------------------------------------- 1 | <!doctype html><html lang="en-US"><head><meta charset="UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=Edge"/><meta name="viewport" content="width=device-width,initial-scale=1"><title>Natural Language Proccessing Functions using ChatGPT
Analytics in Motion Logo

ChatGPT in Excel

Discover awesome ChatGPT features you can use in Excel!

  • Conduct content moderation checks
=CHATGPT.MODERATION(A2)
  • Rapidly classify the sentiment of text
=CHATGPT.SENTIMENT(A2)

These formulas make direct callouts to ChatGPT an return the results into the worksheet almost instantly!

Excel ChatGPT NLP Function Example

For any questions about this add-in feel free to email us directly at: pi@analyticsinmotion.com

-------------------------------------------------------------------------------- /dist/taskpane.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see taskpane.js.LICENSE.txt */ 2 | !function(){"use strict";var t,e,r,n,o,i={27091:function(t){t.exports=function(t,e){return e||(e={}),t?(t=String(t.__esModule?t.default:t),e.hash&&(t+=e.hash),e.maybeNeedQuotes&&/[\t\n\f\r "'=<>`]/.test(t)?'"'.concat(t,'"'):t):t}},30183:function(t,e,r){t.exports=r.p+"assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png"},14762:function(t,e,r){t.exports=r.p+"a698c8dfdb3534564726.svg"},60806:function(t,e,r){t.exports=r.p+"768cb1c2bbf0fa2c9e2d.css"}},a={};function c(t){var e=a[t];if(void 0!==e)return e.exports;var r=a[t]={exports:{}};return i[t](r,r.exports,c),r.exports}c.m=i,c.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return c.d(e,{a:e}),e},c.d=function(t,e){for(var r in e)c.o(e,r)&&!c.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),c.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},function(){var t;c.g.importScripts&&(t=c.g.location+"");var e=c.g.document;if(!t&&e&&(e.currentScript&&(t=e.currentScript.src),!t)){var r=e.getElementsByTagName("script");r.length&&(t=r[r.length-1].src)}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),c.p=t}(),c.b=document.baseURI||self.location.href,function(){function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(){e=function(){return r};var r={},n=Object.prototype,o=n.hasOwnProperty,i=Object.defineProperty||function(t,e,r){t[e]=r.value},a="function"==typeof Symbol?Symbol:{},c=a.iterator||"@@iterator",u=a.asyncIterator||"@@asyncIterator",s=a.toStringTag||"@@toStringTag";function f(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{f({},"")}catch(t){f=function(t,e,r){return t[e]=r}}function l(t,e,r,n){var o=e&&e.prototype instanceof d?e:d,a=Object.create(o.prototype),c=new S(n||[]);return i(a,"_invoke",{value:E(t,r,c)}),a}function h(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}r.wrap=l;var p={};function d(){}function y(){}function v(){}var g={};f(g,c,(function(){return this}));var m=Object.getPrototypeOf,w=m&&m(m(k([])));w&&w!==n&&o.call(w,c)&&(g=w);var b=v.prototype=d.prototype=Object.create(g);function x(t){["next","throw","return"].forEach((function(e){f(t,e,(function(t){return this._invoke(e,t)}))}))}function L(e,r){function n(i,a,c,u){var s=h(e[i],e,a);if("throw"!==s.type){var f=s.arg,l=f.value;return l&&"object"==t(l)&&o.call(l,"__await")?r.resolve(l.__await).then((function(t){n("next",t,c,u)}),(function(t){n("throw",t,c,u)})):r.resolve(l).then((function(t){f.value=t,c(f)}),(function(t){return n("throw",t,c,u)}))}u(s.arg)}var a;i(this,"_invoke",{value:function(t,e){function o(){return new r((function(r,o){n(t,e,r,o)}))}return a=a?a.then(o,o):o()}})}function E(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return{value:void 0,done:!0}}for(r.method=o,r.arg=i;;){var a=r.delegate;if(a){var c=_(a,r);if(c){if(c===p)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var u=h(t,e,r);if("normal"===u.type){if(n=r.done?"completed":"suspendedYield",u.arg===p)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n="completed",r.method="throw",r.arg=u.arg)}}}function _(t,e){var r=e.method,n=t.iterator[r];if(void 0===n)return e.delegate=null,"throw"===r&&t.iterator.return&&(e.method="return",e.arg=void 0,_(t,e),"throw"===e.method)||"return"!==r&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+r+"' method")),p;var o=h(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,p;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,p):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,p)}function O(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function j(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function S(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(O,this),this.reset(!0)}function k(t){if(t){var e=t[c];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,n=function e(){for(;++r=0;--n){var i=this.tryEntries[n],a=i.completion;if("root"===i.tryLoc)return r("end");if(i.tryLoc<=this.prev){var c=o.call(i,"catchLoc"),u=o.call(i,"finallyLoc");if(c&&u){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&o.call(n,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),j(r),p}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;j(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:k(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),p}},r}function r(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function n(t){return function(){var e=this,n=arguments;return new Promise((function(o,i){var a=t.apply(e,n);function c(t){r(a,o,i,c,u,"next",t)}function u(t){r(a,o,i,c,u,"throw",t)}c(void 0)}))}}function o(){return i.apply(this,arguments)}function i(){return i=n(e().mark((function t(){return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,t.next=3,Excel.run(function(){var t=n(e().mark((function t(r){var n;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return(n=r.workbook.getSelectedRange()).load("address"),n.format.fill.color="yellow",t.next=5,r.sync();case 5:console.log("The range address was ".concat(n.address,"."));case 6:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}());case 3:t.next=8;break;case 5:t.prev=5,t.t0=t.catch(0),console.error(t.t0);case 8:case"end":return t.stop()}}),t,null,[[0,5]])}))),i.apply(this,arguments)}Office.onReady((function(){document.getElementById("sideload-msg").style.display="none",document.getElementById("app-body").style.display="flex",document.getElementById("run").onclick=o}))}(),t=c(27091),e=c.n(t),r=new URL(c(60806),c.b),n=new URL(c(14762),c.b),o=new URL(c(30183),c.b),e()(r),e()(n),e()(o)}(); 3 | //# sourceMappingURL=taskpane.js.map -------------------------------------------------------------------------------- /dist/taskpane.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ 2 | -------------------------------------------------------------------------------- /dist/taskpane.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"taskpane.js","mappings":";iCAEIA,EACAC,EACAC,uBCFJC,EAAOC,QAAU,SAAUC,EAAKC,GAM9B,OALKA,IAEHA,EAAU,CAAC,GAGRD,GAKLA,EAAME,OAAOF,EAAIG,WAAaH,EAAII,QAAUJ,GAExCC,EAAQI,OAEVL,GAAOC,EAAQI,MAGbJ,EAAQK,iBAAmB,oBAAoBC,KAAKP,GAC/C,IAAKQ,OAAOR,EAAK,KAGnBA,GAfEA,CAgBX,0OCxBIS,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAab,QAGrB,IAAID,EAASW,EAAyBE,GAAY,CAGjDZ,QAAS,CAAC,GAOX,OAHAe,EAAoBH,GAAUb,EAAQA,EAAOC,QAASW,GAG/CZ,EAAOC,OACf,CAGAW,EAAoBK,EAAID,ECxBxBJ,EAAoBM,EAAI,SAASlB,GAChC,IAAImB,EAASnB,GAAUA,EAAOK,WAC7B,WAAa,OAAOL,EAAgB,OAAG,EACvC,WAAa,OAAOA,CAAQ,EAE7B,OADAY,EAAoBQ,EAAED,EAAQ,CAAEE,EAAGF,IAC5BA,CACR,ECNAP,EAAoBQ,EAAI,SAASnB,EAASqB,GACzC,IAAI,IAAIC,KAAOD,EACXV,EAAoBY,EAAEF,EAAYC,KAASX,EAAoBY,EAAEvB,EAASsB,IAC5EE,OAAOC,eAAezB,EAASsB,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAG3E,ECPAX,EAAoBiB,EAAI,WACvB,GAA0B,iBAAfC,WAAyB,OAAOA,WAC3C,IACC,OAAOC,MAAQ,IAAIC,SAAS,cAAb,EAGhB,CAFE,MAAOC,GACR,GAAsB,iBAAXC,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBtB,EAAoBY,EAAI,SAASW,EAAKC,GAAQ,OAAOX,OAAOY,UAAUC,eAAeC,KAAKJ,EAAKC,EAAO,aCAtG,IAAII,EACA5B,EAAoBiB,EAAEY,gBAAeD,EAAY5B,EAAoBiB,EAAEa,SAAW,IACtF,IAAIC,EAAW/B,EAAoBiB,EAAEc,SACrC,IAAKH,GAAaG,IACbA,EAASC,gBACZJ,EAAYG,EAASC,cAAcC,MAC/BL,GAAW,CACf,IAAIM,EAAUH,EAASI,qBAAqB,UACzCD,EAAQE,SAAQR,EAAYM,EAAQA,EAAQE,OAAS,GAAGH,IAC5D,CAID,IAAKL,EAAW,MAAM,IAAIS,MAAM,yDAChCT,EAAYA,EAAUU,QAAQ,OAAQ,IAAIA,QAAQ,QAAS,IAAIA,QAAQ,YAAa,KACpFtC,EAAoBuC,EAAIX,KCfxB5B,EAAoBwC,EAAIT,SAASU,SAAWC,KAAKZ,SAASa,mQCC1D,ovNAaO,SAAeC,IAAG,+BAoBxB,kCApBM,uGAEGC,MAAMD,IAAG,6BAAC,WAAOE,GAAO,uEAUO,OAN7BC,EAAQD,EAAQE,SAASC,oBAGzBC,KAAK,WAGXH,EAAMI,OAAOC,KAAKC,MAAQ,SAAS,SAE7BP,EAAQQ,OAAM,OACpBC,QAAQC,IAAI,yBAAD,OAA0BT,EAAMU,QAAO,MAAK,2CACxD,mDAdc,IAcb,sDAEFF,QAAQG,MAAM,EAAD,IAAQ,wDAExB,wBA1BDC,OAAOC,SAAQ,WACb7B,SAAS8B,eAAe,gBAAgBC,MAAMC,QAAU,OACxDhC,SAAS8B,eAAe,YAAYC,MAAMC,QAAU,OACpDhC,SAAS8B,eAAe,OAAOG,QAAUpB,CAC3C,2BTVI3D,EAA6B,IAAIgF,IAAI,cACrC/E,EAA6B,IAAI+E,IAAI,cACrC9E,EAA6B,IAAI8E,IAAI,cAEH,IAAyChF,GACzC,IAAyCC,GACzC,IAAyCC","sources":["webpack://excel-custom-functions-js/./src/taskpane/taskpane.html","webpack://excel-custom-functions-js/./node_modules/html-loader/dist/runtime/getUrl.js","webpack://excel-custom-functions-js/webpack/bootstrap","webpack://excel-custom-functions-js/webpack/runtime/compat get default export","webpack://excel-custom-functions-js/webpack/runtime/define property getters","webpack://excel-custom-functions-js/webpack/runtime/global","webpack://excel-custom-functions-js/webpack/runtime/hasOwnProperty shorthand","webpack://excel-custom-functions-js/webpack/runtime/publicPath","webpack://excel-custom-functions-js/webpack/runtime/jsonp chunk loading","webpack://excel-custom-functions-js/./src/taskpane/taskpane.js"],"sourcesContent":["// Imports\nimport ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from \"../../node_modules/html-loader/dist/runtime/getUrl.js\";\nvar ___HTML_LOADER_IMPORT_0___ = new URL(\"./taskpane.css\", import.meta.url);\nvar ___HTML_LOADER_IMPORT_1___ = new URL(\"../../assets/logo.svg\", import.meta.url);\nvar ___HTML_LOADER_IMPORT_2___ = new URL(\"../../assets/analytics-in-motion-chatgpt-in-excel-add-in-example.png\", import.meta.url);\n// Module\nvar ___HTML_LOADER_REPLACEMENT_0___ = ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___(___HTML_LOADER_IMPORT_0___);\nvar ___HTML_LOADER_REPLACEMENT_1___ = ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___(___HTML_LOADER_IMPORT_1___);\nvar ___HTML_LOADER_REPLACEMENT_2___ = ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___(___HTML_LOADER_IMPORT_2___);\nvar code = \" Natural Language Proccessing Functions using ChatGPT <\" + \"script src=\\\"https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js\\\"><\" + \"/script>
\\\"Analytics

ChatGPT in Excel

Discover awesome ChatGPT features you can use in Excel!

  • Conduct content moderation checks
=CHATGPT.MODERATION(A2)
  • Rapidly classify the sentiment of text
=CHATGPT.SENTIMENT(A2)

These formulas make direct callouts to ChatGPT an return the results into the worksheet almost instantly!

\\\"Excel

For any questions about this add-in feel free to email us directly at: pi@analyticsinmotion.com

\";\n// Exports\nexport default code;","\"use strict\";\n\nmodule.exports = function (url, options) {\n if (!options) {\n // eslint-disable-next-line no-param-reassign\n options = {};\n }\n\n if (!url) {\n return url;\n } // eslint-disable-next-line no-underscore-dangle, no-param-reassign\n\n\n url = String(url.__esModule ? url.default : url);\n\n if (options.hash) {\n // eslint-disable-next-line no-param-reassign\n url += options.hash;\n }\n\n if (options.maybeNeedQuotes && /[\\t\\n\\f\\r \"'=<>`]/.test(url)) {\n return \"\\\"\".concat(url, \"\\\"\");\n }\n\n return url;\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) scriptUrl = scripts[scripts.length - 1].src\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","__webpack_require__.b = document.baseURI || self.location.href;\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t41: 0\n};\n\n// no chunk on demand loading\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// no jsonp function","/*\n * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n * See LICENSE in the project root for license information.\n */\n\n/* global console, document, Excel, Office */\n\n// The initialize function must be run each time a new page is loaded\nOffice.onReady(() => {\n document.getElementById(\"sideload-msg\").style.display = \"none\";\n document.getElementById(\"app-body\").style.display = \"flex\";\n document.getElementById(\"run\").onclick = run;\n});\n\nexport async function run() {\n try {\n await Excel.run(async (context) => {\n /**\n * Insert your Excel code here\n */\n const range = context.workbook.getSelectedRange();\n\n // Read the range address\n range.load(\"address\");\n\n // Update the fill color\n range.format.fill.color = \"yellow\";\n\n await context.sync();\n console.log(`The range address was ${range.address}.`);\n });\n } catch (error) {\n console.error(error);\n }\n}\n"],"names":["___HTML_LOADER_IMPORT_0___","___HTML_LOADER_IMPORT_1___","___HTML_LOADER_IMPORT_2___","module","exports","url","options","String","__esModule","default","hash","maybeNeedQuotes","test","concat","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","m","n","getter","d","a","definition","key","o","Object","defineProperty","enumerable","get","g","globalThis","this","Function","e","window","obj","prop","prototype","hasOwnProperty","call","scriptUrl","importScripts","location","document","currentScript","src","scripts","getElementsByTagName","length","Error","replace","p","b","baseURI","self","href","run","Excel","context","range","workbook","getSelectedRange","load","format","fill","color","sync","console","log","address","error","Office","onReady","getElementById","style","display","onclick","URL"],"sourceRoot":""} -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticsinmotion/chatgpt-sentiment-analysis-in-excel/233b5be58e8b54c15446683f4b472c4ef8656b45/favicon.ico -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1546c5f2-73ca-4e9e-9736-9803319e6c89 4 | 1.0.0.0 5 | Analytics in Motion 6 | en-US 7 | 8 | 9 | 10 | 11 | 12 | 13 | https://www.analyticsinmotion.com 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ReadWriteDocument 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | <Description resid="GetStarted.Description"/> 51 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 52 | </GetStarted> 53 | <FunctionFile resid="Taskpane.Url"/> 54 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 55 | <!--<OfficeTab id="TabHome">--> 56 | <CustomTab id="AICopilot.Tab"> 57 | <Group id="AICopilot.Tab.Group1"> 58 | <Label resid="AICopilot.Tab.Group1.Label"/> 59 | <Icon> 60 | <bt:Image size="16" resid="Icon.16x16"/> 61 | <bt:Image size="32" resid="Icon.32x32"/> 62 | <bt:Image size="80" resid="Icon.80x80"/> 63 | </Icon> 64 | <Control xsi:type="Button" id="TaskpaneButton"> 65 | <Label resid="TaskpaneButton.Label"/> 66 | <Supertip> 67 | <Title resid="TaskpaneButton.Label"/> 68 | <Description resid="TaskpaneButton.Tooltip"/> 69 | </Supertip> 70 | <Icon> 71 | <bt:Image size="16" resid="Icon.16x16"/> 72 | <bt:Image size="32" resid="Icon.32x32"/> 73 | <bt:Image size="80" resid="Icon.80x80"/> 74 | </Icon> 75 | <Action xsi:type="ShowTaskpane"> 76 | <TaskpaneId>ButtonId1</TaskpaneId> 77 | <SourceLocation resid="Taskpane.Url"/> 78 | </Action> 79 | </Control> 80 | </Group> 81 | 82 | <Label resid="AICopilot.Tab.Label"/> 83 | 84 | </CustomTab> 85 | <!--</OfficeTab>--> 86 | </ExtensionPoint> 87 | </DesktopFormFactor> 88 | </Host> 89 | </Hosts> 90 | <Resources> 91 | <bt:Images> 92 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-nlp-16.png"/> 93 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-nlp-32.png"/> 94 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-nlp-80.png"/> 95 | </bt:Images> 96 | <bt:Urls> 97 | <bt:Url id="Functions.Script.Url" DefaultValue="https://localhost:3000/public/functions.js"/> 98 | <bt:Url id="Functions.Metadata.Url" DefaultValue="https://localhost:3000/public/functions.json"/> 99 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/> 100 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/> 101 | </bt:Urls> 102 | <bt:ShortStrings> 103 | <bt:String id="Functions.Namespace" DefaultValue="CHATGPT"/> 104 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/> 105 | <bt:String id="AICopilot.Tab.Group1.Label" DefaultValue="ChatGPT"/> 106 | <bt:String id="TaskpaneButton.Label" DefaultValue="NLP Functions"/> 107 | <bt:String id="AICopilot.Tab.Label" DefaultValue="AI Copilot"/> 108 | </bt:ShortStrings> 109 | <bt:LongStrings> 110 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started."/> 111 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/> 112 | </bt:LongStrings> 113 | </Resources> 114 | </VersionOverrides> 115 | </OfficeApp> -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excel-custom-functions-js", 3 | "version": "0.0.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/OfficeDev/Excel-Custom-Functions-JS.git" 7 | }, 8 | "license": "MIT", 9 | "engines": { 10 | "node": ">=16 <19", 11 | "npm": ">=7 <10" 12 | }, 13 | "scripts": { 14 | "build": "webpack --mode production", 15 | "build:dev": "webpack --mode development", 16 | "dev-server": "webpack serve --mode development", 17 | "lint": "office-addin-lint check", 18 | "lint:fix": "office-addin-lint fix", 19 | "prestart": "npm run build", 20 | "prettier": "office-addin-lint prettier", 21 | "start": "office-addin-debugging start manifest.xml", 22 | "start:desktop": "office-addin-debugging start manifest.xml desktop", 23 | "start:web": "office-addin-debugging start manifest.xml web", 24 | "stop": "office-addin-debugging stop manifest.xml", 25 | "test:e2e": "mocha -r ts-node/register test/end-to-end/*.ts", 26 | "test:unit": "mocha -r ts-node/register test/unit/*.test.ts", 27 | "validate": "office-addin-manifest validate manifest.xml", 28 | "watch": "webpack --mode development --watch" 29 | }, 30 | "dependencies": { 31 | "core-js": "^3.9.1", 32 | "regenerator-runtime": "^0.13.7" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.13.16", 36 | "@babel/polyfill": "^7.12.1", 37 | "@babel/preset-env": "^7.15.6", 38 | "@babel/preset-typescript": "7.15.0", 39 | "@types/custom-functions-runtime": "^1.6.4", 40 | "@types/find-process": "1.2.0", 41 | "@types/office-js": "^1.0.256", 42 | "@types/office-runtime": "^1.0.23", 43 | "acorn": "^8.5.0", 44 | "babel-loader": "^8.3.0", 45 | "copy-webpack-plugin": "^9.0.1", 46 | "custom-functions-metadata-plugin": "^1.4.5", 47 | "dotenv-webpack": "^8.0.1", 48 | "eslint-plugin-office-addins": "^2.1.4", 49 | "file-loader": "^6.2.0", 50 | "html-loader": "^4.1.0", 51 | "html-webpack-plugin": "^5.5.0", 52 | "office-addin-cli": "^1.5.4", 53 | "office-addin-debugging": "^5.0.2", 54 | "office-addin-dev-certs": "^1.11.1", 55 | "office-addin-lint": "^2.2.4", 56 | "office-addin-manifest": "^1.12.0", 57 | "office-addin-prettier-config": "^1.2.0", 58 | "os-browserify": "^0.3.0", 59 | "process": "^0.11.10", 60 | "request": "^2.88.2", 61 | "source-map-loader": "^3.0.0", 62 | "ts-loader": "^9.4.1", 63 | "typescript": "^4.3.5", 64 | "webpack": "^5.76.0", 65 | "webpack-cli": "^4.8.0", 66 | "webpack-dev-server": "4.7.4" 67 | }, 68 | "prettier": "office-addin-prettier-config", 69 | "browserslist": [ 70 | "ie 11" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /src/commands/commands.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | /* global global, Office, self, window */ 7 | 8 | Office.onReady(() => { 9 | // If needed, Office.js is ready to be called 10 | }); 11 | 12 | /** 13 | * Shows a notification when the add-in command is executed. 14 | * @param event 15 | */ 16 | function action(event) { 17 | const message = { 18 | type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage, 19 | message: "Performed action.", 20 | icon: "Icon.80x80", 21 | persistent: true, 22 | }; 23 | 24 | // Show a notification message 25 | Office.context.mailbox.item.notificationMessages.replaceAsync("action", message); 26 | 27 | // Be sure to indicate when the add-in command function is complete 28 | event.completed(); 29 | } 30 | 31 | function getGlobal() { 32 | return typeof self !== "undefined" 33 | ? self 34 | : typeof window !== "undefined" 35 | ? window 36 | : typeof global !== "undefined" 37 | ? global 38 | : undefined; 39 | } 40 | 41 | const g = getGlobal(); 42 | 43 | // the add-in command functions need to be available in global scope 44 | g.action = action; 45 | -------------------------------------------------------------------------------- /src/functions/functions.js: -------------------------------------------------------------------------------- 1 | /* global clearInterval, console, setInterval */ 2 | 3 | /** 4 | * Content Moderation using ChatGPT 5 | * @customfunction 6 | * @param {string} text The text to be checked 7 | * @returns {string} Result whether the modertaion has passed or failed. 8 | */ 9 | export function moderation(text) { 10 | const url = "https://api.openai.com/v1/moderations"; 11 | const apikey = process.env.OPENAI_API_KEY; 12 | const method = "POST"; 13 | const model = "text-moderation-latest"; 14 | return new Promise(function (resolve, reject) { 15 | if (!text) { 16 | resolve("Blank"); 17 | } 18 | fetch(url, { 19 | method: method, 20 | headers: { 21 | 'Content-Type': 'application/json', 22 | 'Authorization': 'Bearer ' + apikey 23 | }, 24 | body: JSON.stringify({ 25 | 'model': model, 26 | 'input': text 27 | }) 28 | }) 29 | .then(function (response){ 30 | return response.json(); 31 | } 32 | ) 33 | .then(function (json) { 34 | const response = JSON.stringify(json.results[0].flagged); 35 | let result = ""; 36 | let cellcolor = ""; 37 | if (response === "false") { 38 | result = "Passed"; 39 | cellcolor = "green"; 40 | } else if (response === "true") { 41 | result = "Failed"; 42 | cellcolor = "red"; 43 | } else { 44 | result = "Error"; 45 | } 46 | resolve(result); 47 | }) 48 | }) 49 | } 50 | 51 | 52 | /** 53 | * Sentiment Analysis using ChatGPT 54 | * @customfunction 55 | * @param {string} text The text to be analyzed 56 | * @returns {string} Result whether the text is Positive, Negative or Neutral. 57 | */ 58 | export function sentiment(text) { 59 | const url = "https://api.openai.com/v1/completions"; 60 | const apikey = process.env.OPENAI_API_KEY; 61 | const method = "POST"; 62 | const model = "text-curie-001"; 63 | const prefix = "Decide whether a Tweet's sentiment is positive, neutral, or negative. Tweet:"; 64 | const suffix = "Sentiment: "; 65 | return new Promise(function (resolve, reject) { 66 | if (!text) { 67 | resolve("Blank"); 68 | } 69 | fetch(url, { 70 | method: method, 71 | headers: { 72 | 'Content-Type': 'application/json', 73 | 'Authorization': 'Bearer ' + apikey 74 | }, 75 | body: JSON.stringify({ 76 | 'model': model, 77 | 'prompt': prefix + text + suffix, 78 | 'max_tokens': 20, 79 | 'temperature': 0, 80 | 'top_p': 1, 81 | 'frequency_penalty': 0.5, 82 | 'presence_penalty': 0, 83 | 'n': 1 84 | }) 85 | }) 86 | .then(function (response){ 87 | return response.json(); 88 | } 89 | ) 90 | .then(function (json) { 91 | const response = JSON.stringify(json.choices[0].text); 92 | let response2 = response.replace(/\\n/g, '').replace(/['"]+/g, ''); 93 | let response3 = response2.toLowerCase().charAt(0).toUpperCase() + response2.slice(1); 94 | resolve(response3); 95 | }) 96 | }) 97 | } -------------------------------------------------------------------------------- /src/taskpane/taskpane.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | html, 7 | body { 8 | width: 100%; 9 | height: 100%; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | ul { 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | .ms-welcome__header { 20 | padding: 10px; 21 | padding-bottom: 20px; 22 | padding-top: 35px; 23 | display: -webkit-flex; 24 | display: flex; 25 | -webkit-flex-direction: column; 26 | flex-direction: column; 27 | align-items: center; 28 | } 29 | 30 | .ms-welcome__main { 31 | display: -webkit-flex; 32 | display: flex; 33 | -webkit-flex-direction: column; 34 | flex-direction: column; 35 | -webkit-flex-wrap: nowrap; 36 | flex-wrap: nowrap; 37 | -webkit-align-items: center; 38 | align-items: center; 39 | -webkit-flex: 1 0 0; 40 | flex: 1 0 0; 41 | padding: 10px 20px; 42 | } 43 | 44 | .ms-welcome__main > h2 { 45 | width: 100%; 46 | text-align: center; 47 | } 48 | 49 | .ms-welcome__features { 50 | list-style-type: none; 51 | margin-top: 20px; 52 | } 53 | 54 | .ms-welcome__features.ms-List .ms-ListItem { 55 | padding-bottom: 20px; 56 | display: -webkit-flex; 57 | display: flex; 58 | } 59 | 60 | .ms-welcome__features.ms-List .ms-ListItem > .ms-Icon { 61 | margin-right: 10px; 62 | } 63 | 64 | .ms-welcome__action.ms-Button--hero { 65 | margin-top: 30px; 66 | } 67 | 68 | .ms-Button.ms-Button--hero .ms-Button-label { 69 | color: #0078d7; 70 | } 71 | 72 | .ms-Button.ms-Button--hero:hover .ms-Button-label, 73 | .ms-Button.ms-Button--hero:focus .ms-Button-label{ 74 | color: #005a9e; 75 | cursor: pointer; 76 | } 77 | 78 | b { 79 | font-weight: bold; 80 | } 81 | 82 | .ms-font-su { 83 | font-size: 32px; 84 | } 85 | 86 | code { 87 | font-family: monospace; 88 | font-size: inherit; 89 | background: #ffeff0; 90 | word-wrap: break-word; 91 | padding: .1rem .3rem .2rem; 92 | border-radius: .2rem; 93 | } 94 | 95 | .footer{ 96 | background:#2a2a2a; 97 | padding:10px 0px; 98 | font-family: 'Play', sans-serif; 99 | text-align:center; 100 | } 101 | 102 | .footer .row{ 103 | width:100%; 104 | margin:1% 0%; 105 | padding:0.6% 0%; 106 | color:gray; 107 | font-size:0.8em; 108 | } 109 | 110 | .footer .row a{ 111 | text-decoration:none; 112 | color:gray; 113 | transition:0.5s; 114 | } 115 | 116 | .footer .row a:hover{ 117 | color:#fff; 118 | } 119 | 120 | .footer .row ul{ 121 | width:100%; 122 | } 123 | 124 | .footer .row a i{ 125 | font-size:2em; 126 | margin:0% 5%; 127 | } 128 | -------------------------------------------------------------------------------- /src/taskpane/taskpane.html: -------------------------------------------------------------------------------- 1 | <!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. --> 2 | 3 | <!DOCTYPE html> 4 | <html lang="en-US"> 5 | 6 | <head> 7 | <meta charset="UTF-8" /> 8 | <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> 9 | <meta name="viewport" content="width=device-width, initial-scale=1"> 10 | <title>Natural Language Proccessing Functions using ChatGPT 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | Analytics in Motion Logo 29 |

ChatGPT in Excel

30 |
31 |
32 |

Discover awesome ChatGPT features you can use in Excel!

33 |
    34 |
  • 35 | 36 | Conduct content moderation checks 37 |
  • 38 |
39 | =CHATGPT.MODERATION(A2) 40 |
    41 |
  • 42 | 43 | Rapidly classify the sentiment of text 44 |
  • 45 |
46 | =CHATGPT.SENTIMENT(A2) 47 |
48 |

These formulas make direct callouts to ChatGPT an return the results into the worksheet almost instantly!

49 | Excel ChatGPT NLP Function Example 50 | 51 |

For any questions about this add-in feel free to email us directly at: pi@analyticsinmotion.com 52 |

53 |
54 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/taskpane/taskpane.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | /* global console, document, Excel, Office */ 7 | 8 | // The initialize function must be run each time a new page is loaded 9 | Office.onReady(() => { 10 | document.getElementById("sideload-msg").style.display = "none"; 11 | document.getElementById("app-body").style.display = "flex"; 12 | document.getElementById("run").onclick = run; 13 | }); 14 | 15 | export async function run() { 16 | try { 17 | await Excel.run(async (context) => { 18 | /** 19 | * Insert your Excel code here 20 | */ 21 | const range = context.workbook.getSelectedRange(); 22 | 23 | // Read the range address 24 | range.load("address"); 25 | 26 | // Update the fill color 27 | range.format.fill.color = "yellow"; 28 | 29 | await context.sync(); 30 | console.log(`The range address was ${range.address}.`); 31 | }); 32 | } catch (error) { 33 | console.error(error); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "baseUrl": ".", 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "jsx": "react", 8 | "noEmitOnError": true, 9 | "outDir": "lib", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "lib": [ 13 | "es2015", 14 | "dom" 15 | ], 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | "dist", 22 | "lib", 23 | "lib-amd" 24 | ], 25 | "ts-node": { 26 | "files": true 27 | } 28 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | const devCerts = require("office-addin-dev-certs"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const CustomFunctionsMetadataPlugin = require("custom-functions-metadata-plugin"); 6 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 7 | const path = require("path"); 8 | 9 | const Dotenv = require('dotenv-webpack'); 10 | 11 | const urlDev = "https://localhost:3000/"; 12 | const urlProd = "https://www.analyticsinmotion.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION 13 | 14 | /* global require, module, process, __dirname */ 15 | 16 | async function getHttpsOptions() { 17 | const httpsOptions = await devCerts.getHttpsServerOptions(); 18 | return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert }; 19 | } 20 | 21 | module.exports = async (env, options) => { 22 | const dev = options.mode === "development"; 23 | const config = { 24 | devtool: "source-map", 25 | entry: { 26 | polyfill: ["core-js/stable", "regenerator-runtime/runtime"], 27 | taskpane: ["./src/taskpane/taskpane.js", "./src/taskpane/taskpane.html"], 28 | commands: "./src/commands/commands.js", 29 | functions: "./src/functions/functions.js", 30 | }, 31 | output: { 32 | clean: true, 33 | }, 34 | resolve: { 35 | extensions: [".html", ".js"], 36 | }, 37 | module: { 38 | rules: [ 39 | { 40 | test: /\.js$/, 41 | exclude: /node_modules/, 42 | use: { 43 | loader: "babel-loader", 44 | options: { 45 | presets: ["@babel/preset-env"], 46 | }, 47 | }, 48 | }, 49 | { 50 | test: /\.html$/, 51 | exclude: /node_modules/, 52 | use: "html-loader", 53 | }, 54 | { 55 | test: /\.(png|jpg|jpeg|gif|ico)$/, 56 | type: "asset/resource", 57 | generator: { 58 | filename: "assets/[name][ext][query]", 59 | }, 60 | }, 61 | ], 62 | }, 63 | plugins: [ 64 | new Dotenv(), 65 | new CustomFunctionsMetadataPlugin({ 66 | output: "functions.json", 67 | input: "./src/functions/functions.js", 68 | }), 69 | new HtmlWebpackPlugin({ 70 | filename: "taskpane.html", 71 | template: "./src/taskpane/taskpane.html", 72 | chunks: ["polyfill", "taskpane", "functions", "commands"], 73 | }), 74 | new CopyWebpackPlugin({ 75 | patterns: [ 76 | { 77 | from: "assets/*", 78 | to: "assets/[name][ext][query]", 79 | }, 80 | { 81 | from: "manifest*.xml", 82 | to: "[name]" + "[ext]", 83 | transform(content) { 84 | if (dev) { 85 | return content; 86 | } else { 87 | return content.toString().replace(new RegExp(urlDev + "(?:public/)?", "g"), urlProd); 88 | } 89 | }, 90 | }, 91 | ], 92 | }), 93 | ], 94 | devServer: { 95 | static: { 96 | directory: path.join(__dirname, "dist"), 97 | publicPath: "/public", 98 | }, 99 | headers: { 100 | "Access-Control-Allow-Origin": "*", 101 | }, 102 | server: { 103 | type: "https", 104 | options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(), 105 | }, 106 | port: process.env.npm_package_config_dev_server_port || 3000, 107 | }, 108 | }; 109 | 110 | return config; 111 | }; 112 | --------------------------------------------------------------------------------