Canvas ChatGPT

Adds ChatGPT into Instructure Canvas

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Canvas ChatGPT
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Adds ChatGPT into Instructure Canvas
// @author       Riley Campbell
// @match        *.instructure.com/*
// @license      https://opensource.org/license/bsd-3-clause/
// ==/UserScript==
(async function() {
    'use strict';
    if (window.location.href.indexOf("instructure.com") !== -1) {
        if (!window.location.href.includes("conversations")) {
            setInterval(function() {
                window.defaultMessage = {
                    "role": "system",
                    "content": localStorage.getItem('AIprompt')
                }
            }, 1000)
            let prompt = localStorage.getItem('AIprompt')
            if (prompt == null) {
                prompt = ''
                localStorage.setItem('AIprompt', '')
            }
            window.defaultMessage = {
                "role": "system",
                "content": prompt
            }
            window.AImessages = [window.defaultMessage]
            window.renderMessages = function() {
                let container = document.getElementById('container')
                container.innerHTML = ''
                for (let i in window.AImessages) {
                    let message = window.AImessages[i]
                    if (message.role == "system") {
                        continue
                    }
                    container.innerHTML += '<table><tr><th><p>' + message.role.charAt(0).toUpperCase() + message.role.slice(1) + ': </p><button onclick="let message=document.querySelectorAll(`th[data-message-id='+"'"+i.toString()+"'"+']`)[0];message.innerHTML=`<textarea style=\'display: block; width: 95%; height: 100px; resize: none; margin-left: auto; margin-right: auto; margin-top:10px;\'>`+message.innerHTML+`</textarea><button onclick='+"'let message=this.parentElement;let text=message.getElementsByTagName(\\`textarea\\`)[0].value;window.AImessages[parseInt(message.getAttribute(\\`data-message-id\\`))].content=text;message.innerHTML=text'"+'>Save</button>`">Edit</button></th><th data-message-id="'+i.toString()+'" style="white-space: break-spaces;">' + message.content + '</th></tr></table><hr class="solid">'
                }
                window.scrollTo({
                    top: document.body.scrollHeight,
                    behavior: 'smooth'
                });
            }
            window.sendMessage = async function() {
                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">'
                let message = {
                    "role": "user",
                    "content": document.getElementById('myTextArea').value
                }
                window.AImessages.push(message)
                document.getElementById('myTextArea').value = ''
                let messages = [window.defaultMessage, message]
                if (document.getElementById('memory').checked) {
                    messages = window.AImessages
                }
                let response = await fetch('https://api.openai.com/v1/chat/completions', {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": `Bearer ` + localStorage.getItem('openaiAPIKey')
                    },
                    body: JSON.stringify({
                        "model": localStorage.getItem('AImodel'),
                        "messages": messages,
                        "temperature": 1
                    })
                });
 
                if (response.ok) {
                    let result = await response.json()
                    window.AImessages.push(result.choices[0].message)
                    window.renderMessages()
                } else {
                    document.getElementById('container').innerHTML = `Error ${response.status}: ${response.statusText} <button onclick="document.getElementById('myTextArea').value=window.AImessages.pop().content;window.sendMessage()">Retry</button>`;
                };
            }
            window.populateModels = async function() {
                let supportedModels = ['gpt-4', 'gpt-4-0314', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-3.5-turbo', 'gpt-3.5-turbo-0301']
                let response = await fetch('https://api.openai.com/v1/models', {
                    method: "GET",
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": `Bearer ` + localStorage.getItem('openaiAPIKey')
                    },
                });
                if (!response.ok) {
                    return 0;
                }
                let options = await response.json()
                let item = null
                let element = null
                let models = document.getElementById('models')
                models.innerHTML = ''
                for (let i = 0; i < options.data.length; i++) {
                    item = options.data[i]
                    if (supportedModels.includes(item.id)) {
                        element = document.createElement("option")
                        element.value = item.id
                        element.innerHTML = item.id
                        models.appendChild(element)
                    }
                }
            }
            let elem = document.createElement('div')
            elem.id='chatGPTbox'
            elem.setAttribute('style','margin-left:auto;margin-right:auto;width:95%')
            document.getElementById('wrapper').appendChild(elem)
            document.getElementById('chatGPTbox').innerHTML = `
<style>
    .switch {
        position: relative;
        display: inline-block;
        width: 60px;
        height: 34px
    }
 
    .switch input {
        opacity: 0;
        width: 0;
        height: 0
    }
 
    .slider {
        position: absolute;
        cursor: pointer;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: #ccc;
        -webkit-transition: .4s;
        transition: .4s
    }
 
    .slider:before {
        position: absolute;
        content: "";
        height: 26px;
        width: 26px;
        left: 4px;
        bottom: 4px;
        background-color: #fff;
        -webkit-transition: .4s;
        transition: .4s
    }
 
    input:checked+.slider {
        background-color: #2196F3
    }
 
    input:focus+.slider {
        box-shadow: 0 0 1px #2196F3
    }
 
    input:checked+.slider:before {
        -webkit-transform: translateX(26px);
        -ms-transform: translateX(26px);
        transform: translateX(26px)
    }
 
    .slider.round {
        border-radius: 34px
    }
 
    .slider.round:before {
        border-radius: 50%
    }
 
    input[type="checkbox"] {
        display: none;
    }
 
    .wrap-collabsible {
        margin: 1.2rem 0;
    }
 
    .lbl-toggle {
        display: block;
        font-weight: bold;
        font-family: monospace;
        font-size: 1.2rem;
        text-transform: uppercase;
        text-align: center;
        padding: 1rem;
        color: #DDD;
        background: #0069ff;
        cursor: pointer;
        border-radius: 7px;
        transition: all 0.25s ease-out;
    }
 
    .lbl-toggle:hover {
        color: #FFF;
    }
 
    .lbl-toggle::before {
        content: " ";
        display: inline-block;
        border-top: 5px solid transparent;
        border-bottom: 5px solid transparent;
        border-left: 5px solid currentColor;
        vertical-align: middle;
        margin-right: .7rem;
        transform: translateY(-2px);
        transition: transform .2s ease-out;
    }
 
    .toggle:checked+.lbl-toggle::before {
        transform: rotate(90deg) translateX(-3px);
    }
 
    .collapsible-content {
        max-height: 0px;
        overflow: hidden;
        transition: max-height .25s ease-in-out;
    }
 
    .toggle:checked+.lbl-toggle+.collapsible-content {
        max-height: 350px;
    }
 
    .toggle:checked+.lbl-toggle {
        border-bottom-right-radius: 0;
        border-bottom-left-radius: 0;
    }
 
    .collapsible-content .content-inner {
        background: rgba(0, 105, 255, .2);
        border-bottom: 1px solid rgba(0, 105, 255, .45);
        border-bottom-left-radius: 7px;
        border-bottom-right-radius: 7px;
        padding: .5rem 1rem;
    }
 
    .collapsible-content p {
        margin-bottom: 0;
    }
</style>
<div class="ic-DashboardCard" style="padding:10px; color: ; margin-bottom:10px; width: 100%;margin-left:auto;margin-right:auto;">
    <div width=100% height=650px style="padding:10px;" id="container"></div>
    <textarea style="display: block; width: 95%; height: 100px; resize: none; margin-left: auto; margin-right: auto; margin-top:10px;" id="myTextArea"></textarea>
    <br>
    <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>
    <br>
    <div class="wrap-collabsible">
        <input id="collapsible" class="toggle" type="checkbox">
        <label for="collapsible" class="lbl-toggle">Settings</label>
        <div class="collapsible-content">
            <div class="content-inner">
                <table style="max-height:34px;">
                    <tr style="max-height:34px;">
                        <td>
                            <label style="display:block; max-height:34px;" class="switch">
                                <input id="memory" type="checkbox">
                                <span class="slider round"></span>
                            </label>
                        </td>
                        <td>
                            <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>
                        </td>
                    </tr>
                </table>
                <br>
                <label for="models">Model:</label>
                <select name="models" id="models"></select>
                <br>
                <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>
                <br>
                <input id="key" placeholder="API Key" type="text" style="display:block;float:left;"></input>
                <button class="Button button-sidebar-wide" id="setAPIKey" style="display: block;">Set API Key</button>
            </div>
        </div>
    </div>
</div>
        `
            await window.populateModels()
            document.getElementById('models').value = localStorage.getItem('AImodel')
            let checked = (localStorage.getItem('AImemory') === 'true')
            document.getElementById('memory').checked = checked
            setInterval(function() {
                localStorage.setItem('AImemory', document.getElementById('memory').checked)
                localStorage.setItem('AImodel', document.getElementById('models').value)
                localStorage.setItem('AIprompt', document.getElementById('systemPrompt').value)
            }, 1000)
            document.getElementById('submitButton').onclick = async () => {
                window.AImessages = [window.defaultMessage]
                window.renderMessages()
            };
            document.getElementById('setAPIKey').onclick = async () => {
                localStorage.setItem('openaiAPIKey', document.getElementById('key').value)
                document.getElementById('key').value = ''
                await window.populateModels()
            };
            const myTextarea = document.getElementById("myTextArea");
            myTextarea.addEventListener("keydown", async function(event) {
                if (event.key === "Enter" && !event.shiftKey) {
                    event.preventDefault();
                    await window.sendMessage()
                }
            })
        };
    }
})();