├── .DS_Store ├── LICENSE.md ├── README.md ├── _locales ├── en │ └── messages.json └── it │ └── messages.json ├── manifest.json └── src ├── api └── openAI.js ├── assets └── icons │ ├── 128.png │ ├── 48.png │ └── settings.png ├── background.js ├── main.js ├── popup.html └── styles └── main.css /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-diop/ads-gpt/cd393b715a08cd14d692a65d115140b2c01d094f/.DS_Store -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Omar Diop 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ads GPT 2 | 3 | Ads GPT is a free and open source Google Chrome extension that helps marketing professionals and entrepreneurs generate headlines and body text for Facebook ads with ease. With Ads GPT, you can quickly create captivating and targeted ads that will improve your presence on Facebook. 4 | 5 | ## Features 6 | 7 | - Generate headlines and body text for Facebook ads starting from a simple product or service description provided by the user. 8 | - User-friendly interface to help you create ads with ease. 9 | - Ability to generate captivating and targeted ads that will improve your presence on Facebook. 10 | 11 | ## Installation 12 | 13 | There are two ways to install Ads GPT: 14 | 15 | ### From the Chrome Web Store 16 | 17 | To install Ads GPT from the Chrome Web Store, follow these steps: 18 | 19 | Go to the Chrome Web Store. 20 | 21 | 1. Search for "Ads GPT". 22 | 2. Click on the "Add to Chrome" button. 23 | 3. When prompted, click on "Add Extension" to install Ads GPT. 24 | 4. Once installed, you can access Ads GPT from the Chrome menu or by clicking on the Ads GPT icon in the Chrome toolbar. 25 | 26 | ### As an Unpacked Extension 27 | 28 | To install Ads GPT as an unpacked extension, follow these steps: 29 | 30 | 1. Clone or download the source code from the GitHub repository. 31 | 2. Go to the "chrome://extensions" URL in Google Chrome. 32 | 3. Turn on the "Developer mode" toggle switch. 33 | 4. Click on the "Load unpacked" button. 34 | 5. Select the directory where you have cloned or extracted the source code. 35 | 6. Once installed, you can access Ads GPT from the Chrome menu or by clicking on the Ads GPT icon in the Chrome toolbar. 36 | 37 | ## Contribute 38 | 39 | Ads GPT is an open source project and we welcome contributions from the community. If you encounter any issues or have ideas for improvements, please report them on [GitHub](https://github.com/omar-diop/ads-copy-genius/issues). 40 | 41 | ## License 42 | 43 | Ads GPT is licensed under the [**MIT** License](/LICENSE.md) by Omar Diop. 44 | -------------------------------------------------------------------------------- /_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Ads GPT" 4 | }, 5 | "appDesc": { 6 | "message": "Elevate your advertising game." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /_locales/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Ads GPT" 4 | }, 5 | "appDesc": { 6 | "message": "Migliora le tue Ads." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "author": "Omar Diop", 4 | "description": "__MSG_appDesc__", 5 | "default_locale": "en", 6 | "version": "0.0.5", 7 | "manifest_version": 3, 8 | "background.service_worker": { 9 | "scripts": ["background.js"], 10 | "persistent": false 11 | }, 12 | "permissions": ["storage"], 13 | "action": { 14 | "default_popup": "/src/popup.html", 15 | "default_icon": { 16 | "48": "/src/assets/icons/48.png", 17 | "128": "/src/assets/icons/128.png" 18 | } 19 | }, 20 | "icons": { 21 | "48": "/src/assets/icons/48.png", 22 | "128": "/src/assets/icons/128.png" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/openAI.js: -------------------------------------------------------------------------------- 1 | const API_URL = "https://api.openai.com/v1/chat/completions" 2 | 3 | function generateHeadlines(description, language) { 4 | const localizedPrompt = { 5 | it: `Trova cinque headlines con angle diversi per il seguente prodotto da sponsorizzare tramite Facebook Ads:\n\nProdotto:${description}. Formatta il risultato in un elenco puntato.`, 6 | en: `Find five headlines with different marketing angles for the following product to sponsor through Facebook Ads:\n\nProduct:${description}. Format the result in a bullet-point list.`, 7 | } 8 | return postData(API_URL, { 9 | model: "gpt-4", 10 | messages: [{ role: "user", content: localizedPrompt[language] }], 11 | temperature: 0.7, 12 | }) 13 | } 14 | 15 | function generateBodies(description, language) { 16 | const localizedPrompt = { 17 | it: `Trova tre body text con angle diversi per il seguente prodotto da sponsorizzare tramite Facebook Ads:\n\nProdotto:${description}. Formatta il risultato in un elenco puntato.`, 18 | en: `Find three body texts with different angles for the following product to sponsor through Facebook Ads:\n\nProduct:${description}. Format the result in a bullet-point list.`, 19 | } 20 | return postData(API_URL, { 21 | model: "gpt-4", 22 | messages: [{ role: "user", content: localizedPrompt[language] }], 23 | temperature: 0.7, 24 | }) 25 | } 26 | 27 | async function postData(url = "", data = {}) { 28 | const { apiKey } = await chrome.storage.local.get("apiKey") 29 | const response = await fetch(url, { 30 | method: "POST", 31 | mode: "cors", 32 | cache: "no-cache", 33 | headers: { 34 | "Content-Type": "application/json", 35 | Authorization: `Bearer ${apiKey}`, 36 | }, 37 | body: JSON.stringify(data), 38 | }) 39 | return response.json() 40 | } 41 | -------------------------------------------------------------------------------- /src/assets/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-diop/ads-gpt/cd393b715a08cd14d692a65d115140b2c01d094f/src/assets/icons/128.png -------------------------------------------------------------------------------- /src/assets/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-diop/ads-gpt/cd393b715a08cd14d692a65d115140b2c01d094f/src/assets/icons/48.png -------------------------------------------------------------------------------- /src/assets/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-diop/ads-gpt/cd393b715a08cd14d692a65d115140b2c01d094f/src/assets/icons/settings.png -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-diop/ads-gpt/cd393b715a08cd14d692a65d115140b2c01d094f/src/background.js -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const mainView = document.getElementById("main") 3 | const authView = document.getElementById("auth") 4 | const settingsButton = document.getElementById("settings-button") 5 | const logoButton = document.getElementById("logo-container") 6 | const submitButton = document.getElementById("submit-button") 7 | const resetButton = document.getElementById("reset-button") 8 | const submitApiKeyButton = document.getElementById("submit-key-button") 9 | const headlinesResult = document.querySelector(".headlines-result") 10 | const bodiesResult = document.querySelector(".bodies-result") 11 | const headlinesLoader = document.querySelector(".headlines-loader") 12 | const bodiesLoader = document.querySelector(".bodies-loader") 13 | const bodiesError = document.querySelector(".bodies-error") 14 | const headlinesError = document.querySelector(".headlines-error") 15 | 16 | chrome.storage.local.get("apiKey").then(({ apiKey }) => { 17 | if (apiKey) { 18 | mainView.style.display = "block" 19 | 20 | chrome.storage.session.get("savedStade").then(({ savedStade }) => { 21 | if (savedStade) { 22 | const { productDescription, headlines, bodies, language } = savedStade 23 | 24 | setDescription(productDescription) 25 | setLanguage(language) 26 | setHeadlines(headlines) 27 | setBodies(bodies) 28 | } 29 | }) 30 | } else { 31 | authView.style.display = "block" 32 | console.log("no auth") 33 | } 34 | }) 35 | 36 | //Buttons Events 37 | 38 | logoButton.addEventListener("click", () => { 39 | chrome.storage.local.get("apiKey").then(({ apiKey }) => { 40 | if (apiKey) { 41 | authView.style.display = "none" 42 | mainView.style.display = "block" 43 | } 44 | }) 45 | }) 46 | 47 | settingsButton.addEventListener("click", () => { 48 | const apiKeyInput = document.getElementById("apiKey") 49 | chrome.storage.local.get("apiKey").then(({ apiKey }) => { 50 | if (apiKey) { 51 | apiKeyInput.value = apiKey 52 | } 53 | }) 54 | 55 | authView.style.display = "block" 56 | mainView.style.display = "none" 57 | }) 58 | 59 | submitButton.addEventListener("click", () => { 60 | const description = document.getElementById("textarea").value 61 | const language = document.getElementById("language").value 62 | 63 | headlinesLoader.style.display = "block" 64 | bodiesLoader.style.display = "block" 65 | headlinesResult.style.display = "none" 66 | bodiesResult.style.display = "none" 67 | bodiesError.style.display = "none" 68 | headlinesError.style.display = "none" 69 | 70 | let session = { 71 | productDescription: description, 72 | headlines: null, 73 | bodies: null, 74 | language: language, 75 | } 76 | 77 | generateHeadlines(description, language) 78 | .then((data) => { 79 | const output = cleanOutput(data.choices[0].message.content) 80 | session = { ...session, headlines: output } 81 | chrome.storage.session.set({ 82 | savedStade: session, 83 | }) 84 | setHeadlines(output) 85 | }) 86 | .catch((error) => { 87 | headlinesLoader.style.display = "none" 88 | headlinesError.style.display = "block" 89 | console.log(error) 90 | }) 91 | 92 | generateBodies(description, language) 93 | .then((data) => { 94 | const output = cleanOutput(data.choices[0].message.content) 95 | session = { ...session, bodies: output } 96 | chrome.storage.session.set({ 97 | savedStade: session, 98 | }) 99 | setBodies(output) 100 | }) 101 | .catch((error) => { 102 | bodiesLoader.style.display = "none" 103 | bodiesError.style.display = "block" 104 | console.log(error) 105 | }) 106 | }) 107 | 108 | resetButton.addEventListener("click", () => { 109 | headlinesLoader.style.display = "none" 110 | bodiesLoader.style.display = "none" 111 | headlinesResult.style.display = "none" 112 | bodiesResult.style.display = "none" 113 | document.getElementById("textarea").value = "" 114 | chrome.storage.session.set({ 115 | savedStade: { 116 | productDescription: null, 117 | headlines: null, 118 | bodies: null, 119 | language: "en", 120 | }, 121 | }) 122 | }) 123 | 124 | submitApiKeyButton.addEventListener("click", () => { 125 | const apiKey = document.getElementById("apiKey").value 126 | 127 | chrome.storage.local 128 | .set({ 129 | apiKey, 130 | }) 131 | .then(() => { 132 | authView.style.display = "none" 133 | mainView.style.display = "block" 134 | console.log("Saved API Key") 135 | }) 136 | }) 137 | }) 138 | 139 | function setLanguage(language) { 140 | if (!language) return 141 | document.getElementById("language").value = language 142 | } 143 | 144 | function setDescription(description) { 145 | if (!description) return 146 | document.getElementById("textarea").value = description 147 | } 148 | 149 | function setHeadlines(headlines) { 150 | if (!headlines) return 151 | const headlinesResult = document.querySelector(".headlines-result") 152 | const headlinesLoader = document.querySelector(".headlines-loader") 153 | const headlinesDiv = document.querySelector("#headlines") 154 | headlinesDiv.innerText = headlines 155 | headlinesLoader.style.display = "none" 156 | headlinesResult.style.display = "block" 157 | } 158 | 159 | function setBodies(bodies) { 160 | if (!bodies) return 161 | const bodiesResult = document.querySelector(".bodies-result") 162 | const bodiesLoader = document.querySelector(".bodies-loader") 163 | const bodiesDiv = document.querySelector("#body-texts") 164 | bodiesDiv.innerText = bodies 165 | bodiesLoader.style.display = "none" 166 | bodiesResult.style.display = "block" 167 | } 168 | 169 | function cleanOutput(text) { 170 | return text.trim() 171 | } 172 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 | 12 |

Ads GPT

13 |
14 |
15 |
16 |
17 |
18 |

19 | In order to start generating copy you need an OpenAI API Key. 20 |

Don't know how to get one? 21 | Get yours here! 27 |

28 | 33 |
34 |
35 | 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 |

Ads GPT

47 |
48 | 51 |
52 |
53 |
54 |
55 | 56 | 60 | 61 | 66 |
67 |
68 | 69 | 70 |
71 |
72 |
73 |
74 |

Generating headlines...

75 |
76 |
77 |
78 |
79 |
80 |
81 |

Headlines

82 |

83 |
84 |
85 |

Headlines

86 |

An error occurred, please try again.

87 |
88 |
89 |

Generating body texts...

90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |

Body Texts

99 |

100 |
101 |
102 |

Body Texts

103 |

An error occurred, please try again.

104 |
105 |
106 |
107 |
108 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 350px; 3 | margin: 0; 4 | } 5 | 6 | .inner { 7 | padding: 1rem; 8 | } 9 | 10 | #main, 11 | #auth { 12 | display: none; 13 | } 14 | 15 | h1 { 16 | font-size: 1.2rem; 17 | margin: 0; 18 | margin-bottom: 10px; 19 | color: #262626; 20 | } 21 | 22 | .link { 23 | font-weight: bold; 24 | color: rgb(18, 163, 130); 25 | text-decoration: none; 26 | } 27 | 28 | .link:hover { 29 | text-decoration: underline; 30 | } 31 | 32 | .form { 33 | display: flex; 34 | flex-direction: column; 35 | } 36 | 37 | .authDesc { 38 | font-size: 0.9rem; 39 | } 40 | 41 | .button { 42 | font-family: Roboto, system-ui, sans-serif; 43 | align-items: center; 44 | border: 1px solid rgb(218, 220, 224); 45 | border-radius: 4px; 46 | box-sizing: border-box; 47 | color: rgb(18, 163, 130); 48 | cursor: pointer; 49 | display: inline-flex; 50 | flex-shrink: 0; 51 | font-weight: 500; 52 | height: 32px; 53 | justify-content: center; 54 | min-width: 5.14em; 55 | outline-width: 0; 56 | padding: 8px 16px; 57 | position: relative; 58 | user-select: none; 59 | background: white; 60 | margin: 10px 0 5px 10px; 61 | } 62 | 63 | .buttons { 64 | display: flex; 65 | align-items: center; 66 | justify-content: flex-end; 67 | } 68 | 69 | .button:hover { 70 | background: rgba(66, 133, 244, 0.04); 71 | border-color: 210, 227, 252; 72 | } 73 | 74 | .button.primary { 75 | background: rgb(18, 163, 130); 76 | color: #fff; 77 | border-color: rgb(18, 163, 130); 78 | } 79 | 80 | .button.primary:hover { 81 | background: rgba(18, 163, 130, 0.9); 82 | box-shadow: 0 1px 2px 0 rgba(66, 133, 244, 0.3), 83 | 0 1px 3px 1px rgba(66, 133, 244, 0.15); 84 | } 85 | 86 | .header { 87 | display: flex; 88 | flex-direction: row; 89 | align-items: center; 90 | justify-content: space-between; 91 | background-color: rgba(18, 163, 130, 0.2); 92 | padding: 0.8rem; 93 | } 94 | 95 | .logoContainer img { 96 | width: 40px; 97 | height: 40px; 98 | } 99 | 100 | .header h1 { 101 | margin: 0; 102 | margin-left: 0.5rem; 103 | } 104 | 105 | textarea, 106 | select { 107 | border: 2px solid rgba(18, 163, 130, 0.4); 108 | padding: 8px; 109 | border-radius: 4px; 110 | display: block; 111 | margin-top: 5px; 112 | margin-bottom: 10px; 113 | resize: vertical; 114 | } 115 | 116 | textarea:focus, 117 | select:focus { 118 | outline: none; 119 | border: 2px solid rgba(18, 163, 130, 0.8); 120 | } 121 | 122 | input { 123 | border: 2px solid rgba(18, 163, 130, 0.4); 124 | padding: 8px; 125 | border-radius: 4px; 126 | margin-bottom: 1rem; 127 | } 128 | 129 | .buttons p { 130 | margin: 10px 0 5px 10px; 131 | } 132 | 133 | .skeleton { 134 | animation: skeleton-loading 1s linear infinite alternate; 135 | } 136 | 137 | @keyframes skeleton-loading { 138 | 0% { 139 | background-color: hsl(200, 20%, 80%); 140 | } 141 | 100% { 142 | background-color: hsl(200, 20%, 95%); 143 | } 144 | } 145 | 146 | .skeleton-text { 147 | width: 100%; 148 | height: 0.7rem; 149 | margin-bottom: 0.5rem; 150 | border-radius: 0.25rem; 151 | } 152 | 153 | .headlines-loader, 154 | .bodies-loader, 155 | .bodies-result, 156 | .headlines-result, 157 | .headlines-error, 158 | .bodies-error { 159 | display: none; 160 | } 161 | 162 | .headlines-error, 163 | .bodies-error { 164 | color: rgb(163, 18, 30); 165 | } 166 | 167 | .resultText { 168 | font-size: 0.8rem; 169 | line-height: 1.1rem; 170 | color: #252525; 171 | } 172 | 173 | .logoContainer { 174 | display: flex; 175 | flex-direction: row; 176 | align-items: center; 177 | } 178 | 179 | .logoContainer:hover { 180 | cursor: pointer; 181 | } 182 | 183 | .content { 184 | margin-bottom: 2rem; 185 | } 186 | 187 | .label { 188 | font-weight: 700; 189 | font-size: 0.8rem; 190 | } 191 | 192 | .settingsImage { 193 | width: 25px; 194 | height: 25px; 195 | } 196 | 197 | .settingsButton { 198 | background-color: transparent; 199 | outline: none; 200 | border: none; 201 | cursor: pointer; 202 | } 203 | 204 | .footer { 205 | text-align: center; 206 | background-color: #d3d3d3; 207 | padding: 1rem; 208 | color: #707070; 209 | } 210 | --------------------------------------------------------------------------------