├── .gitignore ├── scripts ├── Clean Clipboard.js └── Translate.js ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # misc 2 | .DS_Store 3 | .env 4 | *.log 5 | -------------------------------------------------------------------------------- /scripts/Clean Clipboard.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: gray; icon-glyph: eraser; 4 | Pasteboard.copyString(''); 5 | Script.complete(); 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scriptable-scripts 2 | Some of my scripts for the app Scriptable. 3 | 4 | To execute the scripts in this repository, you must install the iOS app [Scriptable](https://scriptable.app/) and copy the scripts into your iCloud "Scriptable" folder. 5 | 6 | ## Script: Clean Clipboard 7 | The name speaks for itself. A very simple script, but your clipboard shines afterwards 😉. 8 | 9 | ## Script: Translate 10 | Translate your current clipboard text into another language with [DeepL](https://www.deepl.com/). 11 | 12 | ℹ️ Configuration necessary: 13 | - You need a (paid) API key from DeepL [API](https://www.deepl.com/en/docs-api/) 14 | - Please paste the key into the script (API_AUTH_KEY) 15 | - You can also configure the API_SOURCE_LANG (auto detection is supported), API_TARGET_LANG (default language), TRANSLATION_TARGET (QuickLook or Pasteboard) 16 | 17 | --- 18 | 19 | *scriptable-scripts* is licensed under the 20 | The Unlicense 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /scripts/Translate.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: blue; icon-glyph: language; 4 | // Configuration 5 | const API_URL = 'https://api.deepl.com/v2/translate'; // Required 6 | const API_SOURCE_LANG = ''; // Optional (auto detection is supported) 7 | const API_TARGET_LANG = 'EN'; // Optional (default language) 8 | const API_AUTH_KEY = 'PASTE_YOUR_KEY_HERE'; // Required 9 | const TRANSLATION_TARGET = 'QUICKLOOK'; // QUICKLOOK or PASTEBOARD 10 | 11 | // Docs on https://www.deepl.com/docs-api/translating-text/request 12 | 13 | /* ------------------------------------------------ */ 14 | 15 | const isConfigValid = checkConfig(); 16 | 17 | if (isConfigValid) { 18 | // Start translation 19 | try { 20 | const translated = await translate(); 21 | 22 | if (translated) { 23 | await showOutput(translated); 24 | } 25 | } catch (e) { 26 | await showError(e.message); 27 | } 28 | } 29 | 30 | // Finish script safely 31 | Script.complete(); 32 | 33 | /* ------------------------------------------------ */ 34 | 35 | async function translate() { 36 | const values = await getValues(); 37 | 38 | if (!values) { 39 | return ''; 40 | } 41 | 42 | if (!values.text.length) { 43 | throw new Error('Please enter a text to translate.'); 44 | } 45 | 46 | return reqTranslation(values.text, values.lang); 47 | } 48 | 49 | async function getValues() { 50 | const alert = new Alert(); 51 | alert.title = 'Translate...'; 52 | alert.message = 'Please enter your text and target language.'; 53 | alert.addTextField('Text', Pasteboard.paste()); 54 | alert.addTextField('Language', API_TARGET_LANG); 55 | alert.addCancelAction('Cancel'); 56 | alert.addAction('Translate'); 57 | 58 | const actionIndex = await alert.present(); 59 | 60 | if (actionIndex >= 0) { 61 | const text = alert.textFieldValue(0); 62 | const lang = cleanText( 63 | (alert.textFieldValue(1) || API_TARGET_LANG).toUpperCase(), 64 | ); 65 | 66 | return { text, lang }; 67 | } 68 | 69 | return null; 70 | } 71 | 72 | async function reqTranslation(text, targetLang) { 73 | const payload = cleanText(text); 74 | 75 | const req = new Request(API_URL); 76 | req.method = 'POST'; 77 | req.addParameterToMultipart('auth_key', API_AUTH_KEY); 78 | req.addParameterToMultipart('source_lang', API_SOURCE_LANG); 79 | req.addParameterToMultipart('target_lang', targetLang); 80 | req.addParameterToMultipart('text', payload); 81 | 82 | const json = await req.loadJSON(); 83 | 84 | if (json.message) { 85 | return json.message; 86 | } 87 | 88 | return json.translations[0].text; 89 | } 90 | 91 | function cleanText(text) { 92 | return text.trim(); 93 | } 94 | 95 | function checkConfig() { 96 | let missing = new Set(); 97 | 98 | if (!API_URL) { 99 | missing.add('API_URL'); 100 | } 101 | 102 | if (!API_TARGET_LANG) { 103 | missing.add('API_TARGET_LANG'); 104 | } 105 | 106 | if (!API_AUTH_KEY) { 107 | missing.add('API_AUTH_KEY'); 108 | } 109 | 110 | if (missing.size > 0) { 111 | console.log( 112 | `Please provide values for the following required variables:\n${[ 113 | ...missing, 114 | ].join('\n')}`, 115 | ); 116 | 117 | return false; 118 | } 119 | 120 | return true; 121 | } 122 | 123 | async function showOutput(item) { 124 | if (!item) { 125 | return; 126 | } 127 | 128 | if (TRANSLATION_TARGET === 'PASTEBOARD') { 129 | Pasteboard.copyString(item); 130 | } else { 131 | await QuickLook.present(item); 132 | } 133 | } 134 | 135 | async function showError(msg) { 136 | const alert = new Alert(); 137 | alert.title = '👨‍💻'; 138 | alert.message = msg; 139 | alert.addCancelAction('OK'); 140 | 141 | await alert.present(); 142 | } 143 | --------------------------------------------------------------------------------