Canvas ChatGPT

Adds ChatGPT into Instructure Canvas

当前为 2023-05-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Canvas ChatGPT
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Adds ChatGPT into Instructure Canvas
  6. // @author Riley Campbell
  7. // @match *.instructure.com/*
  8. // @license https://creativecommons.org/licenses/by-nc/4.0/
  9. // ==/UserScript==
  10. (async function() {
  11. 'use strict';
  12. if (window.location.href.indexOf("instructure.com") !== -1) {
  13. if (!window.location.href.includes("conversations")) {
  14. setInterval(function() {
  15. window.defaultMessage = {
  16. "role": "system",
  17. "content": localStorage.getItem('AIprompt')
  18. }
  19. }, 1000)
  20. let prompt = localStorage.getItem('AIprompt')
  21. if (prompt == null) {
  22. prompt = ''
  23. localStorage.setItem('AIprompt', '')
  24. }
  25. window.defaultMessage = {
  26. "role": "system",
  27. "content": prompt
  28. }
  29. window.AImessages = [window.defaultMessage]
  30. window.renderMessages = function() {
  31. let container = document.getElementById('container')
  32. container.innerHTML = ''
  33. for (let message in window.AImessages) {
  34. message = window.AImessages[message]
  35. if (message.role == "system") {
  36. continue
  37. }
  38. container.innerHTML += '<table><tr><th><p>' + message.role.charAt(0).toUpperCase() + message.role.slice(1) + ': </p></th><th style="white-space: break-spaces;">' + message.content + '</th></tr></table><hr class="solid">'
  39. }
  40. window.scrollTo({
  41. top: document.body.scrollHeight,
  42. behavior: 'smooth'
  43. });
  44. }
  45. window.sendMessage = async function() {
  46. document.getElementById('container').innerHTML += '<img style="display: block; width: 20px;margin-left: auto; margin-right: auto; margin-bottom:5px;" src="https://i.gifer.com/origin/34/34338d26023e5515f6cc8969aa027bca_w200.gif">'
  47. let message = {
  48. "role": "user",
  49. "content": document.getElementById('myTextArea').value
  50. }
  51. window.AImessages.push(message)
  52. document.getElementById('myTextArea').value = ''
  53. let messages = [window.defaultMessage, message]
  54. if (document.getElementById('memory').checked) {
  55. messages = window.AImessages
  56. }
  57. let response = await fetch('https://api.openai.com/v1/chat/completions', {
  58. method: "POST",
  59. headers: {
  60. "Content-Type": "application/json",
  61. "Authorization": `Bearer ` + localStorage.getItem('openaiAPIKey')
  62. },
  63. body: JSON.stringify({
  64. "model": localStorage.getItem('AImodel'),
  65. "messages": messages,
  66. "temperature": 1
  67. })
  68. });
  69.  
  70. if (response.ok) {
  71. let result = await response.json()
  72. window.AImessages.push(result.choices[0].message)
  73. window.renderMessages()
  74. } else {
  75. document.getElementById('container').innerHTML = `Error ${response.status}: ${response.statusText}`;
  76. };
  77. }
  78. window.populateModels = async function() {
  79. let supportedModels = ['gpt-4', 'gpt-4-0314', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-3.5-turbo', 'gpt-3.5-turbo-0301']
  80. let response = await fetch('https://api.openai.com/v1/models', {
  81. method: "GET",
  82. headers: {
  83. "Content-Type": "application/json",
  84. "Authorization": `Bearer ` + localStorage.getItem('openaiAPIKey')
  85. },
  86. });
  87. if (!response.ok) {
  88. return 0;
  89. }
  90. let options = await response.json()
  91. let item = null
  92. let element = null
  93. let models = document.getElementById('models')
  94. models.innerHTML = ''
  95. for (let i = 0; i < options.data.length; i++) {
  96. item = options.data[i]
  97. if (supportedModels.includes(item.id)) {
  98. element = document.createElement("option")
  99. element.value = item.id
  100. element.innerHTML = item.id
  101. models.appendChild(element)
  102. }
  103. }
  104. }
  105. let elem = document.createElement('div')
  106. elem.id='chatGPTbox'
  107. elem.setAttribute('style','margin-left:auto;margin-right:auto;width:95%')
  108. document.getElementById('wrapper').appendChild(elem)
  109. document.getElementById('chatGPTbox').innerHTML = `
  110. <style>
  111. .switch {
  112. position: relative;
  113. display: inline-block;
  114. width: 60px;
  115. height: 34px
  116. }
  117.  
  118. .switch input {
  119. opacity: 0;
  120. width: 0;
  121. height: 0
  122. }
  123.  
  124. .slider {
  125. position: absolute;
  126. cursor: pointer;
  127. top: 0;
  128. left: 0;
  129. right: 0;
  130. bottom: 0;
  131. background-color: #ccc;
  132. -webkit-transition: .4s;
  133. transition: .4s
  134. }
  135.  
  136. .slider:before {
  137. position: absolute;
  138. content: "";
  139. height: 26px;
  140. width: 26px;
  141. left: 4px;
  142. bottom: 4px;
  143. background-color: #fff;
  144. -webkit-transition: .4s;
  145. transition: .4s
  146. }
  147.  
  148. input:checked+.slider {
  149. background-color: #2196F3
  150. }
  151.  
  152. input:focus+.slider {
  153. box-shadow: 0 0 1px #2196F3
  154. }
  155.  
  156. input:checked+.slider:before {
  157. -webkit-transform: translateX(26px);
  158. -ms-transform: translateX(26px);
  159. transform: translateX(26px)
  160. }
  161.  
  162. .slider.round {
  163. border-radius: 34px
  164. }
  165.  
  166. .slider.round:before {
  167. border-radius: 50%
  168. }
  169.  
  170. input[type="checkbox"] {
  171. display: none;
  172. }
  173.  
  174. .wrap-collabsible {
  175. margin: 1.2rem 0;
  176. }
  177.  
  178. .lbl-toggle {
  179. display: block;
  180. font-weight: bold;
  181. font-family: monospace;
  182. font-size: 1.2rem;
  183. text-transform: uppercase;
  184. text-align: center;
  185. padding: 1rem;
  186. color: #DDD;
  187. background: #0069ff;
  188. cursor: pointer;
  189. border-radius: 7px;
  190. transition: all 0.25s ease-out;
  191. }
  192.  
  193. .lbl-toggle:hover {
  194. color: #FFF;
  195. }
  196.  
  197. .lbl-toggle::before {
  198. content: " ";
  199. display: inline-block;
  200. border-top: 5px solid transparent;
  201. border-bottom: 5px solid transparent;
  202. border-left: 5px solid currentColor;
  203. vertical-align: middle;
  204. margin-right: .7rem;
  205. transform: translateY(-2px);
  206. transition: transform .2s ease-out;
  207. }
  208.  
  209. .toggle:checked+.lbl-toggle::before {
  210. transform: rotate(90deg) translateX(-3px);
  211. }
  212.  
  213. .collapsible-content {
  214. max-height: 0px;
  215. overflow: hidden;
  216. transition: max-height .25s ease-in-out;
  217. }
  218.  
  219. .toggle:checked+.lbl-toggle+.collapsible-content {
  220. max-height: 350px;
  221. }
  222.  
  223. .toggle:checked+.lbl-toggle {
  224. border-bottom-right-radius: 0;
  225. border-bottom-left-radius: 0;
  226. }
  227.  
  228. .collapsible-content .content-inner {
  229. background: rgba(0, 105, 255, .2);
  230. border-bottom: 1px solid rgba(0, 105, 255, .45);
  231. border-bottom-left-radius: 7px;
  232. border-bottom-right-radius: 7px;
  233. padding: .5rem 1rem;
  234. }
  235.  
  236. .collapsible-content p {
  237. margin-bottom: 0;
  238. }
  239. </style>
  240. <div class="ic-DashboardCard" style="padding:10px; color: ; margin-bottom:10px; width: 100%;margin-left:auto;margin-right:auto;">
  241. <div width=100% height=650px style="padding:10px;" id="container"></div>
  242. <textarea style="display: block; width: 95%; height: 100px; resize: none; margin-left: auto; margin-right: auto; margin-top:10px;" id="myTextArea"></textarea>
  243. <br>
  244. <button class="Button button-sidebar-wide" id=\'submitButton\' style="display: block; width: 95%;margin-left: auto; margin-right: auto; margin-top: 5px; margin-botton: 5px;">Clear History</button>
  245. <br>
  246. <div class="wrap-collabsible">
  247. <input id="collapsible" class="toggle" type="checkbox">
  248. <label for="collapsible" class="lbl-toggle">Settings</label>
  249. <div class="collapsible-content">
  250. <div class="content-inner">
  251. <table style="max-height:34px;">
  252. <tr style="max-height:34px;">
  253. <td>
  254. <label style="display:block; max-height:34px;" class="switch">
  255. <input id="memory" type="checkbox">
  256. <span class="slider round"></span>
  257. </label>
  258. </td>
  259. <td>
  260. <label style="display:block;max-height:34px;text-align: center;margin-top:auto;margin-bottom:auto;">Allow AI to remember past messages. (may incur extra charges)</label>
  261. </td>
  262. </tr>
  263. </table>
  264. <br>
  265. <label for="models">Model:</label>
  266. <select name="models" id="models"></select>
  267. <br>
  268. <textarea style="display: block; width: 95%; height: 100px; resize: none; margin-left: auto; margin-right: auto; margin-top:10px;" placeholder="Default System Prompt (Requires history to be cleared in order to take effect)" id="systemPrompt">` + localStorage.getItem('AIprompt') + `</textarea>
  269. <br>
  270. <input id="key" placeholder="API Key" type="text" style="display:block;float:left;"></input>
  271. <button class="Button button-sidebar-wide" id="setAPIKey" style="display: block;">Set API Key</button>
  272. </div>
  273. </div>
  274. </div>
  275. </div>
  276. `
  277. await window.populateModels()
  278. document.getElementById('models').value = localStorage.getItem('AImodel')
  279. let checked = (localStorage.getItem('AImemory') === 'true')
  280. document.getElementById('memory').checked = checked
  281. setInterval(function() {
  282. localStorage.setItem('AImemory', document.getElementById('memory').checked)
  283. localStorage.setItem('AImodel', document.getElementById('models').value)
  284. localStorage.setItem('AIprompt', document.getElementById('systemPrompt').value)
  285. }, 1000)
  286. document.getElementById('submitButton').onclick = async () => {
  287. window.AImessages = [window.defaultMessage]
  288. window.renderMessages()
  289. };
  290. document.getElementById('setAPIKey').onclick = async () => {
  291. localStorage.setItem('openaiAPIKey', document.getElementById('key').value)
  292. document.getElementById('key').value = ''
  293. await window.populateModels()
  294. };
  295. const myTextarea = document.getElementById("myTextArea");
  296. myTextarea.addEventListener("keydown", async function(event) {
  297. if (event.key === "Enter" && !event.shiftKey) {
  298. event.preventDefault();
  299. await window.sendMessage()
  300. }
  301. })
  302. };
  303. }
  304. })();