Created
July 27, 2025 23:43
-
-
Save pghant/1a60d83a04a087c3cc99c4342770b25b to your computer and use it in GitHub Desktop.
Clash Upgrades Widget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "always_run_in_app" : false, | |
| "icon" : { | |
| "color" : "yellow", | |
| "glyph" : "gavel" | |
| }, | |
| "name" : "Clash Upgrades", | |
| "script" : "const DATA_MAP_URL = 'https:\/\/gist.githubusercontent.com\/pghant\/0717bb1e0e4e0d1373e90bdb3057d9dd\/raw\/dc943f912a7d30cf559349b415c0b4d21c3cad0e\/cocMapping.json';\nconst DATA_MAPPING = await (new Request(DATA_MAP_URL)).loadJSON();\nconst TABLE_WIDTH = {\n COL_1: 140,\n COL_2: 60,\n COL_3: 110\n}\n\nfunction createTable(widget, headerContent, tableContent, colWidths) {\n const fontSize = 12;\n\n const headerRow = widget.addStack();\n headerRow.layoutHorizontally();\n headerContent.forEach((cellContent, idx) => {\n const cell = headerRow.addStack();\n const text = cell.addText(cellContent);\n text.font = Font.mediumSystemFont(fontSize);\n cell.size = new Size(colWidths[idx], 0);\n });\n\n for (const rowContent of tableContent) {\n const row = widget.addStack();\n row.layoutHorizontally();\n rowContent.forEach((cellContent, idx) => {\n const cell = row.addStack();\n if (cellContent.type === 'text') {\n const text = cell.addText(cellContent.data);\n text.font = Font.regularSystemFont(fontSize);\n } else if (cellContent.type === 'date') {\n const date = cell.addDate(cellContent.data);\n date.font = Font.regularSystemFont(fontSize);\n date.applyRelativeStyle();\n date.centerAlignText();\n if (cellContent.data < new Date()) {\n date.textColor = Color.red();\n }\n }\n cell.size = new Size(colWidths[idx], 0);\n });\n }\n}\n\nfunction getCurrentUpgradeRows(upgradeData, timestamp) {\n upgradeData.sort((a, b) => a.timer > b.timer);\n const currentUpgrades = [];\n for (const upgrade of upgradeData) {\n currentUpgrades.push([\n {data: upgrade.name, type: 'text'},\n {data: `${upgrade.level} → ${upgrade.level + 1}`, type: 'text'}, \/\/ TODO: Fix for supercharging once we know max levels\n {data: new Date((timestamp + upgrade.timer) * 1000), type: 'date'}\n ]);\n }\n return currentUpgrades;\n}\n\n\/**\n * Parses Clash of Clans export JSON data. Finds and maps in progress upgrades. Returns an array of upgrades including\n * name, time in seconds remaining, and current level\n *\/\nfunction findInProgress(importedData, type) {\n let inProgress = [];\n if (!!importedData[type]) {\n inProgress = importedData[type]\n .filter(e => !!e.timer)\n .map(e => {\n const elementData = DATA_MAPPING.find(dm => dm.dataId === e.data);\n return {\n name: elementData.name,\n timer: e.timer,\n level: e.lvl,\n supercharge: e.supercharge\n }\n });\n }\n if (type === 'buildings') {\n \/\/ check for in progress crafted defenses\n const craftedDefenseData = importedData[type].find(d => d.data === 1000097);\n if (!!craftedDefenseData) {\n craftedDefenseData.types.forEach(craftedType => {\n craftedType.modules.forEach(craftedTypeModule => {\n if (!!craftedTypeModule.timer) {\n inProgress.push({\n name: 'Crafted Defense', \/\/ TODO: Add individual crafted defense modules to lookup\n timer: craftedTypeModule.timer,\n level: craftedTypeModule.lvl\n });\n }\n });\n });\n }\n }\n\n return inProgress;\n}\n\nasync function getInProgressUpgrades() {\n let importedData;\n const userTagParam = args.widgetParameter ?? 'QCRPYJ8VJ';\n const fm = FileManager.local();\n const documentDirectory = fm.documentsDirectory();\n const clashUpgradeDirectory = fm.joinPath(documentDirectory, 'ClashUpgrades');\n if (!fm.isDirectory(clashUpgradeDirectory)) {\n fm.createDirectory(clashUpgradeDirectory);\n } \n const upgradeFileName = fm.joinPath(clashUpgradeDirectory, `${userTagParam}.json`);\n console.log(upgradeFileName);\n try {\n importedData = JSON.parse(Pasteboard.paste());\n const userTag = importedData.tag.replace('#', '');\n console.log('user tag from export ' + userTag);\n if (userTagParam !== userTag) {\n console.error('User tag mismatch');\n throw new Error('User tag mismatch');\n }\n \/\/ save to new file with the user tag that is passed in\n fm.writeString(upgradeFileName, JSON.stringify(importedData));\n } catch {\n \/\/ check for existing file and pull that instead\n if (fm.fileExists(upgradeFileName)) {\n importedData = JSON.parse(fm.readString(upgradeFileName));\n } else {\n throw new Error('Invalid data');\n }\n }\n\n return {\n inProgress: [\n ...findInProgress(importedData, 'buildings'),\n ...findInProgress(importedData, 'traps'),\n ...findInProgress(importedData, 'heroes'),\n ...findInProgress(importedData, 'units'),\n ...findInProgress(importedData, 'spells'),\n ...findInProgress(importedData, 'siege_machines'),\n ...findInProgress(importedData, 'pets')\n ],\n timestamp: importedData.timestamp\n };\n}\n\nasync function getWidget() {\n const upgrades = await getInProgressUpgrades();\n const widget = new ListWidget();\n const title = widget.addText(`Clash Upgrades - #${args.widgetParameter}`);\n title.centerAlignText();\n title.font = Font.title3();\n widget.addSpacer(8);\n createTable(\n widget,\n ['Name', 'Level', 'Complete'],\n getCurrentUpgradeRows(upgrades.inProgress, upgrades.timestamp),\n [TABLE_WIDTH.COL_1, TABLE_WIDTH.COL_2, TABLE_WIDTH.COL_3]\n );\n\n return widget;\n}\n\ntry {\n const widget = await getWidget();\n if (config.runsInWidget) {\n Script.setWidget(widget);\n } else {\n widget.presentLarge();\n }\n} catch (error) {\n console.error(error);\n const widget = new ListWidget();\n const title = widget.addText('Invalid data for Clash Upgrades');\n title.centerAlignText();\n title.font = Font.title2();\n if (config.runsInWidget) {\n Script.setWidget(widget);\n } else {\n widget.presentLarge();\n }\n}\n\nScript.complete();", | |
| "share_sheet_inputs" : [ | |
| ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment