Skip to content

Commit

Permalink
Merge pull request #7 from marcolivierbouch/v4
Browse files Browse the repository at this point in the history
v4
  • Loading branch information
marcolivierbouch authored Aug 13, 2023
2 parents fa3a03c + 3c709ad commit 4b3fee9
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 15 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The XReplyGPT Chrome Extension is a simple yet powerful tool that automatically


## How it works?
1. Create an OpenAI API key [here](https://platform.openai.com/account/api-keys).
1. Create an OpenAI API key [here](https://platform.openai.com/account/api-keys). Make sure you have a paid account or it won't work.
2. Save it in the config section.
3. Open https://x.com.
4. Press the shortcut CTRL+SHIFT+Q.
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"manifest_version": 3,
"version": "3.0",
"version": "4.0",
"name": "XReplyGPT",
"description": "The fastest way to reply to tweets using ChatGPT and the easiest way to boost your tweet impressions.",
"icons": {
Expand Down
Binary file added releases/v4.0.zip
Binary file not shown.
19 changes: 19 additions & 0 deletions src/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@
<h2 class="text-4xl font-semibold text-black">XReplyGPT</h2>
</div>
<ul class="timeline mt-4">
<!-- Release 4 -->
<li class="mb-6">
<span class="text-black font-semibold text-xl">Version 4.0</span>
<p class="text-gray-600 ml-auto mb-4">August 15, 2023</p>
<!-- Feature Section -->
<div class="ml-12 mb-4">
<div class="bg-green-300 border-2 border-green-600 rounded-lg p-1 inline-block">
<h3 class="text-lg text-gray-800 font-semibold">Features</h3>
</div>
<p class="my-2">🎉 Add api key validation when you change it</p>
<p class="my-2">🎉 Display error if it is impossible to generate tweets</p>
<p class="my-2">🎉 Added a AI model selector</p>
<img src="releases_images/v4Error.PNG" alt="V4 Errors"
class="mt-2 border-2 rounded-lg">
<img src="releases_images/v4Features.PNG" alt="V4 Features"
class="mt-2 border-2 rounded-lg">
</div>
</li>
<hr class="border-gray-400 my-6">
<!-- Release 3 -->
<li class="mb-6">
<span class="text-black font-semibold text-xl">Version 3.0</span>
Expand Down
38 changes: 36 additions & 2 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ if (window.articles) {
console.log(content);

const allRefs = user.querySelectorAll('a');
console.log(allRefs);
if (allRefs.length < 3) {
console.log("Not enough refs");
return;
}
const ref = allRefs[2].getAttribute('href');
const tweetId = ref.split('/')[3];
console.log(tweetId);
Expand All @@ -85,6 +90,9 @@ if (window.articles) {
const apiKey = await chrome.storage.local.get(['open-ai-key']);
const gptQuery = await chrome.storage.local.get(['gpt-query']);

const model = await chrome.storage.local.get(['openai-model']);
console.log(`Using model: ${model['openai-model']}`)

const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
Expand All @@ -96,7 +104,7 @@ if (window.articles) {
{ role: "system", 'content': gptQuery['gpt-query'] || "You are a ghostwriter and reply to the user's tweets by talking directly to the person, you must keep it short, exclude hashtags." },
{ role: "user", 'content': '[username] wrote [tweet]'.replace('[username]', username).replace('[tweet]', content.innerText) }
],
model: "gpt-3.5-turbo",
model: model['openai-model'],
temperature: 1,
max_tokens: 256,
top_p: 1,
Expand All @@ -105,8 +113,34 @@ if (window.articles) {
})
})

if (!response.ok) {
// creates a modal error over twitter content
const errorMessage = "Error while generating a reply for this tweet: " + (await response.json()).error.message;
let p = document.createElement("p");
p.innerHTML = errorMessage;
p.style.marginBottom = '5px';
p.style.marginTop = '5px';
div.appendChild(p);

// Create the button
let button = document.createElement("button");
button.innerText = "Report Issue";
button.classList.add("button");
button.style.display = "flex";
button.style.alignItems = "center";
button.style.marginTop = "10px";
// Add a click event handler to open the GitHub issue URL
button.addEventListener("click", function() {
window.open(`https://github.com/marcolivierbouch/XReplyGPT/issues/new?title=Issue%20while%20generating%20tweet&body=${errorMessage}`);
});

div.appendChild(button);
shadowRoot.appendChild(div);
content.appendChild(shadowRoot);
return;
}

const resp = await response.json()
console.log(resp);

let p = document.createElement("p");
p.innerHTML = "Generated reply: ";
Expand Down
48 changes: 38 additions & 10 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@
transform: rotateZ(0deg);
}
}

#validate-button {
height: 38px; /* Adjust as needed */
}

@keyframes breathing {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}

/* Apply the breathing animation to the button */
#validate-button.invalid {
animation: breathing 1.5s infinite;
}
</style>
<meta charset="UTF-8">
</meta>
Expand Down Expand Up @@ -73,7 +91,7 @@ <h1 class="flex items-center text-xl font-extrabold dark:text-white">How it work
<div class="flex-grow border-t border-gray-400"></div>
</div>
<p class="text-gray-700">1. Create an OpenAI API key <a href="https://platform.openai.com/account/api-keys"
target="_blank" class="underline">here</a>.</p>
target="_blank" class="underline">here</a>. Make sure you have a paid account or it won't work.</p>
<p class="text-gray-700">2. Save it in the config section. </p>
<p class="text-gray-700">3. Open <a class="underline" href="https://x.com" target="_blank">https://x.com</a>.</p>
<p class="text-gray-700">4. Press the shortcut CTRL+SHIFT+Q.</p>
Expand All @@ -86,15 +104,25 @@ <h1 class="flex items-center text-xl font-extrabold dark:text-white">Config ⚙
<div class="flex-grow border-t border-gray-400"></div>
</div>
<div class="max-w-2xl mb-4 mx-auto">
<p class="text-gray-700">OpenAI API key:</p>
<input class="border-2 w-full h-10 pl-2 rounded" type="password" id="api-key"
placeholder="OpenAI platform API key"></input>
<div class="flex items-center">
<input id="show-api-key" type="checkbox" value=""
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="show-api-key" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Show API
key</lable>
</div>
<div class="flex items-center">
<input class="border-2 w-full h-10 pl-2 rounded" type="password" id="api-key"
placeholder="OpenAI platform API key">
<button id="validate-button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold px-4 rounded ml-2">
Validate
</button>
</div>
<div class="flex items-center">
<input id="show-api-key" type="checkbox" value=""
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="show-api-key" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Show API
key</label>
</div>
</div>

<div class="mb-4">
<p class="text-gray-700">OpenAI model:</p>
<select id="models-select" class="border-2 border-gray-300 text-sm rounded focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
</select>
</div>

<p class="text-gray-700">ChatGPT query:</p>
Expand Down
111 changes: 110 additions & 1 deletion src/popup.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,95 @@
function getOpenAIModels(apiKey) {
return fetch('https://api.openai.com/v1/models', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + apiKey
}
}).then(response => response.json());
}

function validateApiKey() {
const apiKey = document.getElementById('api-key').value;
const apiKeyInput = document.getElementById('api-key');
const validateButton = document.getElementById('validate-button');
const selectModels = document.getElementById('models-select');
const gptQueryInput = document.getElementById('gpt-query');

getOpenAIModels(apiKey)
.then((response) => {
if (response['error'] !== undefined) {
apiKeyInput.style.borderColor = 'red';
gptQueryInput.disabled = true;
selectModels.disabled = true;
validateButton.classList.add('invalid');
} else {
apiKeyInput.style.borderColor = 'green';
gptQueryInput.disabled = false;
selectModels.disabled = false;
loadAndPopulateModels()
validateButton.classList.remove('invalid');
}
})
.catch((error) => {
console.error('Error occurred during API key validation:', error);
apiKeyInput.style.borderColor = 'red';
gptQueryInput.disabled = true;
selectModels.disabled = true;
validateButton.classList.add('invalid');
});
}

function loadAndPopulateModels() {
const apiKey = document.getElementById('api-key').value;

getOpenAIModels(apiKey)
.then((response) => {
const modelSelect = document.getElementById('models-select');

// Clear existing options
modelSelect.innerHTML = '';

// Add default option
chrome.storage.local.get(['openai-model']).then((model) => {
const defaultOption = document.createElement('option');
defaultOption.value = model['openai-model'] || 'gpt-4';
defaultOption.text = model['openai-model'] || 'gpt-4';
defaultOption.selected = true;
modelSelect.appendChild(defaultOption);
});

// Add models from response
console.log(response.data);
response.data.forEach(model => {
const option = document.createElement('option');
option.value = model.id;
option.text = model.id;
modelSelect.appendChild(option);
});
})
.catch((error) => {
console.error('Error loading models:', error);
});
}

document.addEventListener('DOMContentLoaded', function () {

// API key save
document.getElementById('api-key').addEventListener('change', function () {
const value = document.getElementById('api-key').value;
console.log(value);
chrome.storage.local.set({ 'open-ai-key': value }).then(() => {
console.log("New API key saved");
});
validateApiKey();
});

document.getElementById('validate-button').addEventListener('click', validateApiKey);

document.getElementById('models-select').addEventListener('change', function () {
const value = document.getElementById('models-select').value;
chrome.storage.local.set({ 'openai-model': value }).then(() => {
console.log("New OpenAI model saved");
});
});

// Query save
Expand Down Expand Up @@ -49,8 +132,34 @@ document.addEventListener('DOMContentLoaded', function () {
chrome.storage.local.get(['open-ai-key']).then((result) => {
if (result['open-ai-key'] == undefined) {
document.getElementById('api-key').value = "";
document.getElementById('validate-button').classList.add('invalid');

const selectModels = document.getElementById('models-select');
const gptQueryInput = document.getElementById('gpt-query');
gptQueryInput.disabled = true;
selectModels.disabled = true;
} else {
document.getElementById('api-key').value = result['open-ai-key'];
validateApiKey();
}
});

chrome.storage.local.get(['openai-model']).then((result) => {
if (result['openai-model'] == undefined) {
chrome.storage.local.set({ 'openai-model': 'gpt-4' })
const modelSelect = document.getElementById('models-select');
const defaultOption = document.createElement('option');
defaultOption.value = 'gpt-4';
defaultOption.text = 'gpt-4';
defaultOption.selected = true;
modelSelect.appendChild(defaultOption);
} else {
const modelSelect = document.getElementById('models-select');
const defaultOption = document.createElement('option');
defaultOption.value = result['openai-model'];
defaultOption.text = result['openai-model'];
defaultOption.selected = true;
modelSelect.appendChild(defaultOption);
}
});

Expand Down
Binary file added src/releases_images/v4Error.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/releases_images/v4Features.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4b3fee9

Please sign in to comment.