├── .appcast.xml ├── .gitignore ├── LICENSE ├── README.md ├── __tests__ └── analytics.test.js ├── assets ├── Icon.png ├── Icon64.png └── placeholder.png ├── docs ├── screen1.png └── screen2.png ├── package.json └── src ├── analytics.js ├── common.js └── manifest.json /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm 2 | node_modules 3 | .npm 4 | npm-debug.log 5 | 6 | ### macOS ### 7 | # General 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | 12 | # build artifacts 13 | vkdata.sketchplugin 14 | 15 | # WebStorm 16 | .idea 17 | 18 | # tests 19 | __tests__ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 VK.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VK Data Sketch Plugin 2 | 3 | ## Installation 4 | 5 | Just [download it from the releases page](https://github.com/VKCOM/vkdata-sketchplugin/releases). 6 | 7 | ## Features 8 | 9 | Get Avatars, names and video thumbnails from your account at vk.com, using Sketch 52’s new Data Supplier feature. 10 | 11 | You can use it from the toolbar Data icon, from the contextual menu for any layer, or even for Overrides using the Inspector. 12 | 13 | ![Using the VK Data Plugin from the toolbar icon](docs/screen1.png) 14 | 15 | ## Install with Sketch Runner 16 | 17 | With Sketch Runner, just go to the `install` command and search for `VK Data`. Runner allows you to manage plugins and do much more to speed up your workflow in Sketch. [Download Runner here](http://www.sketchrunner.com). 18 | 19 | ![Sketch Runner screenshot](docs/screen2.png) -------------------------------------------------------------------------------- /__tests__/analytics.test.js: -------------------------------------------------------------------------------- 1 | // https://github.com/mathieudutour/sketch-module-google-analytics 2 | 3 | const track = require('../src/analytics.js') 4 | 5 | function makeRequest (url) { 6 | return String(url.absoluteString()) 7 | } 8 | 9 | let userId = null 10 | 11 | test('should create a new user Id if not present', () => { 12 | NSUserDefaults.standardUserDefaults().removeObjectForKey("google.analytics.uuid") 13 | const url = track('trackingId', 'event', {}, { makeRequest }) 14 | userId = url.split('cid=')[1].split('&')[0] 15 | expect(userId).toHaveLength(36) 16 | }) 17 | 18 | test('should use the same user Id if already present', () => { 19 | const url = track('trackingId', 'event', {}, { makeRequest }) 20 | const newUserId = url.split('cid=')[1].split('&')[0] 21 | expect(newUserId).toBe(userId) 22 | }) 23 | 24 | test('should debug', () => { 25 | expect(track('trackingId', 'event', {}, { makeRequest, debug: true })).toMatch('https://www.google-analytics.com/debug/collect?v=1&tid=trackingId&ds=Sketch') 26 | expect(String(track('trackingId', 'event', {}, { debug: true }))).toMatch('The value provided for parameter \'tid\' is invalid') 27 | }) -------------------------------------------------------------------------------- /assets/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/vkdata-sketchplugin/dceb913301f2731e6a37992abadbe8922ca06556/assets/Icon.png -------------------------------------------------------------------------------- /assets/Icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/vkdata-sketchplugin/dceb913301f2731e6a37992abadbe8922ca06556/assets/Icon64.png -------------------------------------------------------------------------------- /assets/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/vkdata-sketchplugin/dceb913301f2731e6a37992abadbe8922ca06556/assets/placeholder.png -------------------------------------------------------------------------------- /docs/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/vkdata-sketchplugin/dceb913301f2731e6a37992abadbe8922ca06556/docs/screen1.png -------------------------------------------------------------------------------- /docs/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/vkdata-sketchplugin/dceb913301f2731e6a37992abadbe8922ca06556/docs/screen2.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VK-Data", 3 | "version": "2.4.5", 4 | "skpm": { 5 | "manifest": "src/manifest.json", 6 | "main": "vkdata.sketchplugin", 7 | "assets": [ 8 | "assets/**/*" 9 | ] 10 | }, 11 | "engines": { 12 | "sketch": ">=3.0" 13 | }, 14 | "scripts": { 15 | "build": "skpm-build", 16 | "watch": "skpm-build --watch", 17 | "start": "skpm-build --watch --run", 18 | "postinstall": "npm run build && skpm-link", 19 | "test": "skpm-test" 20 | }, 21 | "devDependencies": { 22 | "@skpm/builder": "^0.7.4", 23 | "@skpm/test-runner": "^0.4.1" 24 | }, 25 | "dependencies": { 26 | "@skpm/fs": "^0.2.5", 27 | "mocha-js-delegate": "^0.2.0", 28 | "sketch-module-google-analytics": "^0.2.1" 29 | }, 30 | "author": "VK Design Team", 31 | "authorEmail": "design@vk.com", 32 | "description": "Plugin for work with the data of your page at vk.com", 33 | "repository": "https://github.com/VKCOM/vkdata-sketchplugin.git", 34 | "homepage": "https://github.com/VKCOM/vkdata-sketchplugin", 35 | "licence": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /src/analytics.js: -------------------------------------------------------------------------------- 1 | // https://github.com/mathieudutour/sketch-module-google-analytics 2 | 3 | let Settings = require('sketch/settings') 4 | 5 | let kUUIDKey = 'google.analytics.uuid' 6 | let uuid = NSUserDefaults.standardUserDefaults().objectForKey(kUUIDKey) 7 | if (!uuid) { 8 | uuid = NSUUID.UUID().UUIDString() 9 | NSUserDefaults.standardUserDefaults().setObject_forKey(uuid, kUUIDKey) 10 | } 11 | 12 | let variant = MSApplicationMetadata.metadata().variant 13 | let source = 'Sketch ' + (variant === 'NONAPPSTORE' ? '' : variant + ' ') + Settings.version.sketch 14 | 15 | function jsonToQueryString (json) { 16 | return Object.keys(json) 17 | .map(function (key) { 18 | return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) 19 | }) 20 | .join('&') 21 | } 22 | 23 | function makeRequest (url, options) { 24 | if (!url) { 25 | return 26 | } 27 | 28 | if (options && options.makeRequest) { 29 | return options.makeRequest(url) 30 | } 31 | if (options && options.debug) { 32 | let request = NSURLRequest.requestWithURL(url) 33 | let responsePtr = MOPointer.alloc().init() 34 | let errorPtr = MOPointer.alloc().init() 35 | 36 | let data = NSURLConnection.sendSynchronousRequest_returningResponse_error(request, responsePtr, errorPtr) 37 | return data ? NSString.alloc().initWithData_encoding(data, NSUTF8StringEncoding) : errorPtr.value() 38 | } 39 | 40 | NSURLSession.sharedSession() 41 | .dataTaskWithURL(url) 42 | .resume() 43 | } 44 | 45 | module.exports = function (trackingId, hitType, props, options) { 46 | let payload = { 47 | v: 1, 48 | tid: trackingId, 49 | ds: source, 50 | cid: uuid, 51 | t: hitType 52 | } 53 | 54 | if (typeof __command !== 'undefined') { 55 | payload.an = __command.pluginBundle().name() 56 | payload.aid = __command.pluginBundle().identifier() 57 | payload.av = __command.pluginBundle().version() 58 | } 59 | 60 | if (props) { 61 | Object.keys(props).forEach(function (key) { 62 | payload[key] = props[key] 63 | }) 64 | } 65 | 66 | let url = NSURL.URLWithString( 67 | 'https://www.google-analytics.com/' + (options && options.debug ? 'debug/' : '') + 'collect?' + jsonToQueryString(payload) + '&z=' + NSUUID.UUID().UUIDString() 68 | ) 69 | 70 | return makeRequest(url, options) 71 | } 72 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | const sketch = require('sketch/dom') 2 | const DataSupplier = require('sketch/data-supplier') 3 | const UI = require('sketch/ui') 4 | const Settings = require('sketch/settings') 5 | const Async = require('sketch/async') 6 | 7 | const os = require('os') 8 | const path = require('path') 9 | const util = require('util') 10 | const fs = require('@skpm/fs') 11 | const MochaJSDelegate = require('mocha-js-delegate') 12 | const track = require('./analytics.js') 13 | 14 | const APP_ID = '6742961' 15 | const REDIRECT_URI = 'https://oauth.vk.com/blank.html' 16 | const SCOPE = 'offline,friends,groups,video' 17 | const API_URI = 'https://api.vk.com/method/' 18 | const API_VERSION = '5.101' 19 | 20 | const ACCESS_TOKEN = Settings.settingForKey('ACCESS_TOKEN') 21 | const USER_ID = Settings.settingForKey('USER_ID') 22 | 23 | const SETTING_KEY = 'vk.photo.id' 24 | const FOLDER = path.join(os.tmpdir(), 'com.vk.data-plugin') 25 | 26 | const DEBUG_MODE = false 27 | 28 | function auth () { 29 | let authURL = 'https://oauth.vk.com/authorize?client_id=' + APP_ID + '&display=page&redirect_uri=' + REDIRECT_URI + '&scope=' + SCOPE + '&response_type=token&v=' + API_VERSION + '&revoke=1' 30 | let panelWidth = 800 31 | let panelHeight = 600 32 | 33 | let fiber = Async.createFiber() 34 | let frame = NSMakeRect(0, 0, panelWidth, panelHeight) 35 | let mask = NSTitledWindowMask + NSClosableWindowMask 36 | let panel = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(frame, mask, NSBackingStoreBuffered, true) 37 | 38 | panel.setBackgroundColor(NSColor.whiteColor()) 39 | 40 | panel.title = 'VK Data Plugin' 41 | panel.titlebarAppearsTransparent = false 42 | 43 | panel.center() 44 | panel.becomeKeyWindow() 45 | panel.makeKeyAndOrderFront(null) 46 | 47 | panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true) 48 | panel.standardWindowButton(NSWindowZoomButton).setHidden(true) 49 | 50 | let config = WKWebViewConfiguration.alloc().init() 51 | config.setWebsiteDataStore(WKWebsiteDataStore.nonPersistentDataStore()) 52 | 53 | let webView = WKWebView.alloc().initWithFrame_configuration(frame, config) 54 | let request = NSURLRequest.requestWithURL(NSURL.URLWithString(authURL)) 55 | 56 | let delegate = new MochaJSDelegate({ 57 | 'webView:didCommitNavigation:': function (webView) { 58 | let components = NSURLComponents.componentsWithURL_resolvingAgainstBaseURL(webView.URL(), false) 59 | // eslint-disable-next-line eqeqeq 60 | if (components.path() == '/blank.html') { 61 | let fragment = components.fragment() 62 | let url = NSURL.URLWithString('https://vk.com/?' + String(fragment)) 63 | let queryItems = NSURLComponents.componentsWithURL_resolvingAgainstBaseURL(url, false).queryItems() 64 | let values = queryItems.reduce(function (prev, item) { 65 | prev[String(item.name())] = String(item.value()) 66 | return prev 67 | }, {}) 68 | 69 | let token = values['access_token'] 70 | let userId = values['user_id'] 71 | 72 | if (token.length > 0) { 73 | Settings.setSettingForKey('ACCESS_TOKEN', token) 74 | Settings.setSettingForKey('USER_ID', userId) 75 | Settings.setSettingForKey('SCOPE_KEY', SCOPE) 76 | 77 | panel.close() 78 | fiber.cleanup() 79 | } 80 | } 81 | } 82 | }) 83 | 84 | webView.setNavigationDelegate(delegate.getClassInstance()) 85 | webView.loadRequest(request) 86 | panel.contentView().addSubview(webView) 87 | } 88 | 89 | export function checkauth () { 90 | (ACCESS_TOKEN === undefined || Settings.settingForKey('SCOPE_KEY') !== SCOPE) ? auth() : UI.message('You can use the plugin') 91 | } 92 | 93 | function sendEvent (category, action, value) { 94 | let analytics = track('UA-130190471-1', 'event', { 95 | ec: category, // the event category 96 | ea: action + ' ' + value // the event action 97 | }, { debug: DEBUG_MODE }) 98 | return analytics 99 | } 100 | 101 | export function logout () { 102 | Settings.setSettingForKey('ACCESS_TOKEN', undefined) 103 | Settings.setSettingForKey('USER_ID', undefined) 104 | Settings.setSettingForKey('SCOPE_KEY', undefined) 105 | auth() 106 | } 107 | 108 | export function onStartup () { 109 | DataSupplier.registerDataSupplier('public.image', 'Your Avatar or..', 'PhotoByUserID') 110 | DataSupplier.registerDataSupplier('public.image', 'Friends: Hints', 'MyFriends') 111 | DataSupplier.registerDataSupplier('public.image', 'Friends: Random', 'MyFriendsRandom') 112 | DataSupplier.registerDataSupplier('public.image', 'Groups: Hints', 'MyGroups') 113 | DataSupplier.registerDataSupplier('public.image', 'Groups: Random', 'MyGroupsRandom') 114 | DataSupplier.registerDataSupplier('public.image', 'Video by..', 'VideoByOwnerID') 115 | DataSupplier.registerDataSupplier('public.image', 'Bookmarks: Users', 'BookmarksUsers') 116 | DataSupplier.registerDataSupplier('public.image', 'Bookmarks: Groups', 'BookmarksGroups') 117 | 118 | DataSupplier.registerDataSupplier('public.text', 'Your Name', 'MyName') 119 | DataSupplier.registerDataSupplier('public.text', 'Friends Hints: First Name', 'MyFriendsFirstNames') 120 | DataSupplier.registerDataSupplier('public.text', 'Friends Hints: Last Name', 'MyFriendsLastNames') 121 | DataSupplier.registerDataSupplier('public.text', 'Friends Hints: Full Name', 'MyFriendsFullNames') 122 | DataSupplier.registerDataSupplier('public.text', 'Friends: Random', 'MyFriendsNamesRandom') 123 | DataSupplier.registerDataSupplier('public.text', 'Groups: Hints', 'MyGroupsNames') 124 | DataSupplier.registerDataSupplier('public.text', 'Groups: Random', 'MyGroupsNamesRandom') 125 | DataSupplier.registerDataSupplier('public.text', 'Video Title by..', 'VideoTitleByOwnerID') 126 | DataSupplier.registerDataSupplier('public.text', 'Video Views by..', 'VideoViewsByOwnerID') 127 | DataSupplier.registerDataSupplier('public.text', 'Bookmarks: Users', 'BookmarksUsersNames') 128 | DataSupplier.registerDataSupplier('public.text', 'Bookmarks: Groups', 'BookmarksGroupsNames') 129 | 130 | if (ACCESS_TOKEN !== undefined) { 131 | getData('stats.trackVisitor', { 132 | 'access_token': ACCESS_TOKEN, 133 | 'v': API_VERSION 134 | }) 135 | } 136 | sendEvent('Launch Sketch') 137 | } 138 | 139 | export function onShutdown () { 140 | DataSupplier.deregisterDataSuppliers() 141 | try { 142 | if (fs.existsSync(FOLDER)) { 143 | fs.rmdirSync(FOLDER) 144 | } 145 | } catch (err) { 146 | console.error(err) 147 | } 148 | } 149 | 150 | export function onPhotoByUserID (context) { 151 | let recentTerm = Settings.sessionVariable('recentTermPhoto') 152 | let ownerId = (recentTerm === undefined || recentTerm.length === 0) ? USER_ID : recentTerm 153 | 154 | UI.getInputFromUser( 155 | 'Enter Account ID of vk.com', 156 | { initialValue: ownerId }, 157 | (error, value) => { 158 | if (error || value.length === 0) { 159 | // UI.message(error) 160 | // sendEvent('Error', 'User Input', error) 161 | // most likely the user canceled the input 162 | } else { 163 | ownerId = value.trim() 164 | Settings.setSessionVariable('recentTermPhoto', ownerId) 165 | 166 | let requestedCount = context.data.requestedCount 167 | getData('users.get', { 168 | 'user_ids': ownerId, 169 | 'fields': 'photo_200,photo_100', 170 | 'access_token': ACCESS_TOKEN, 171 | 'v': API_VERSION 172 | }) 173 | .then(body => { 174 | let dataKey = context.data.key 175 | let items = util.toArray(context.data.items).map(sketch.fromNative) 176 | items.forEach((item, index) => { 177 | if (!item.type) { 178 | item = sketch.Shape.fromNative(item.sketchObject) 179 | } 180 | if (requestedCount > body.response.length) { 181 | let diff = requestedCount - body.response.length 182 | for (let i = 0; i < diff; i++) { 183 | body.response.push(body.response[i]) 184 | } 185 | } 186 | if (!isEmpty(body.response[index].photo_200)) { 187 | process(body.response[index].photo_200, dataKey, index, item) 188 | } else { 189 | process(body.response[index].photo_100, dataKey, index, item) 190 | } 191 | 192 | sendEvent('Friends', 'By User ID', null) 193 | }) 194 | }) 195 | .catch(error => { 196 | UI.message('Something went wrong') 197 | console.error(error) 198 | sendEvent('Error', 'By User ID', error) 199 | }) 200 | } 201 | } 202 | ) 203 | } 204 | 205 | export function onMyFriends (context) { 206 | let selection = context.data.requestedCount 207 | getData('friends.get', { 208 | 'user_id': USER_ID, 209 | 'order': 'hints', 210 | 'fields': 'photo_200,photo_100', 211 | 'access_token': ACCESS_TOKEN, 212 | 'count': selection, 213 | 'v': API_VERSION 214 | }) 215 | .then(body => { 216 | let dataKey = context.data.key 217 | let items = util.toArray(context.data.items).map(sketch.fromNative) 218 | items.forEach((item, index) => { 219 | if (!item.type) { 220 | item = sketch.Shape.fromNative(item.sketchObject) 221 | } 222 | 223 | if (!isEmpty(body.response['items'][index].photo_200)) { 224 | process(body.response['items'][index].photo_200, dataKey, index, item) 225 | } else { 226 | process(body.response['items'][index].photo_100, dataKey, index, item) 227 | } 228 | 229 | sendEvent('Friends', 'Hints', null) 230 | }) 231 | }) 232 | .catch(error => { 233 | UI.message('Something went wrong') 234 | console.error(error) 235 | sendEvent('Error', 'Friends', 'Hints: ' + error) 236 | }) 237 | } 238 | 239 | export function onMyGroups (context) { 240 | let selection = context.data.requestedCount 241 | getData('groups.get', { 242 | 'user_id': USER_ID, 243 | 'access_token': ACCESS_TOKEN, 244 | 'count': selection, 245 | 'extended': 1, 246 | 'v': API_VERSION 247 | }) 248 | .then(body => { 249 | let dataKey = context.data.key 250 | let items = util.toArray(context.data.items).map(sketch.fromNative) 251 | items.forEach((item, index) => { 252 | if (!item.type) { 253 | item = sketch.Shape.fromNative(item.sketchObject) 254 | } 255 | 256 | if (!isEmpty(body.response['items'][index].photo_200)) { 257 | process(body.response['items'][index].photo_200, dataKey, index, item) 258 | } else { 259 | process(body.response['items'][index].photo_100, dataKey, index, item) 260 | } 261 | 262 | sendEvent('Groups', 'Avatar', null) 263 | }) 264 | }) 265 | .catch(error => { 266 | UI.message('Something went wrong') 267 | console.error(error) 268 | sendEvent('Error', 'Groups', 'Avatar:' + error) 269 | }) 270 | } 271 | 272 | export function onMyFriendsFirstNames (context) { 273 | let selection = context.data.requestedCount 274 | getData('friends.get', { 275 | 'user_id': USER_ID, 276 | 'order': 'hints', 277 | 'fields': 'first_name', 278 | 'access_token': ACCESS_TOKEN, 279 | 'count': selection, 280 | 'v': API_VERSION 281 | }) 282 | .then(body => { 283 | let dataKey = context.data.key 284 | let items = util.toArray(context.data.items).map(sketch.fromNative) 285 | items.forEach((item, index) => { 286 | if (!item.type) { 287 | item = sketch.Shape.fromNative(item.sketchObject) 288 | } 289 | 290 | DataSupplier.supplyDataAtIndex(dataKey, body.response['items'][index].first_name, index) 291 | sendEvent('Friends', 'First Names', null) 292 | }) 293 | }) 294 | .catch(error => { 295 | UI.message('Something went wrong') 296 | console.error(error) 297 | sendEvent('Error', 'Friends', 'First Names: ' + error) 298 | }) 299 | } 300 | 301 | export function onMyFriendsLastNames (context) { 302 | let selection = context.data.requestedCount 303 | getData('friends.get', { 304 | 'user_id': USER_ID, 305 | 'order': 'hints', 306 | 'fields': 'last_name', 307 | 'access_token': ACCESS_TOKEN, 308 | 'count': selection, 309 | 'v': API_VERSION 310 | }) 311 | .then(body => { 312 | let dataKey = context.data.key 313 | let items = util.toArray(context.data.items).map(sketch.fromNative) 314 | items.forEach((item, index) => { 315 | if (!item.type) { 316 | item = sketch.Shape.fromNative(item.sketchObject) 317 | } 318 | DataSupplier.supplyDataAtIndex(dataKey, body.response['items'][index].last_name, index) 319 | 320 | sendEvent('Friends', 'Last Names', null) 321 | }) 322 | }) 323 | .catch(error => { 324 | UI.message('Something went wrong') 325 | console.error(error) 326 | sendEvent('Error', 'Friends', 'Last Names: ' + error) 327 | }) 328 | } 329 | 330 | export function onMyFriendsFullNames (context) { 331 | let selection = context.data.requestedCount 332 | getData('friends.get', { 333 | 'user_id': USER_ID, 334 | 'order': 'hints', 335 | 'fields': 'first_name', 336 | 'access_token': ACCESS_TOKEN, 337 | 'count': selection, 338 | 'v': API_VERSION 339 | }) 340 | .then(body => { 341 | let dataKey = context.data.key 342 | let items = util.toArray(context.data.items).map(sketch.fromNative) 343 | items.forEach((item, index) => { 344 | if (!item.type) { 345 | item = sketch.Shape.fromNative(item.sketchObject) 346 | } 347 | let fullName = body.response['items'][index].first_name + ' ' + body.response['items'][index].last_name 348 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 349 | 350 | sendEvent('Friends', 'Full Names', null) 351 | }) 352 | }) 353 | .catch(error => { 354 | UI.message('Something went wrong') 355 | console.error(error) 356 | sendEvent('Error', 'Friends', 'Full Names: ' + error) 357 | }) 358 | } 359 | 360 | export function onMyName (context) { 361 | getData('users.get', { 362 | 'user_ids': USER_ID, 363 | 'fields': 'first_name,last_name', 364 | 'access_token': ACCESS_TOKEN, 365 | 'v': API_VERSION 366 | }) 367 | .then(body => { 368 | let dataKey = context.data.key 369 | let items = util.toArray(context.data.items).map(sketch.fromNative) 370 | items.forEach((item, index) => { 371 | if (!item.type) { 372 | item = sketch.Shape.fromNative(item.sketchObject) 373 | } 374 | 375 | let fullName = body.response[0].first_name + ' ' + body.response[0].last_name 376 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 377 | 378 | sendEvent('User', 'Names', null) 379 | }) 380 | }) 381 | .catch(function (error) { 382 | UI.message('Something went wrong') 383 | console.error(error) 384 | sendEvent('Error', 'User', 'Names: ' + error) 385 | }) 386 | } 387 | 388 | export function onMyGroupsNames (context) { 389 | let selection = context.data.requestedCount 390 | getData('groups.get', { 391 | 'user_id': USER_ID, 392 | 'access_token': ACCESS_TOKEN, 393 | 'count': selection, 394 | 'extended': 1, 395 | 'v': API_VERSION 396 | }) 397 | .then(body => { 398 | let dataKey = context.data.key 399 | let items = util.toArray(context.data.items).map(sketch.fromNative) 400 | items.forEach((item, index) => { 401 | if (!item.type) { 402 | item = sketch.Shape.fromNative(item.sketchObject) 403 | } 404 | 405 | let name = body.response['items'][index].name 406 | DataSupplier.supplyDataAtIndex(dataKey, name, index) 407 | 408 | sendEvent('Groups', 'Names', null) 409 | }) 410 | }) 411 | .catch(error => { 412 | UI.message('Something went wrong') 413 | console.error(error) 414 | sendEvent('Error', 'Groups', 'Names: ' + error) 415 | }) 416 | } 417 | 418 | export function onVideoByOwnerID (context) { 419 | let selection = context.data.requestedCount 420 | let recentTerm = Settings.sessionVariable('recentTermVideo') 421 | let ownerId = (recentTerm === undefined || recentTerm.length === 0) ? USER_ID : recentTerm 422 | UI.getInputFromUser( 423 | 'Enter Video Author ID of vk.com', 424 | { initialValue: ownerId }, 425 | (error, value) => { 426 | if (error || value.length === 0) { 427 | // UI.message(error) 428 | // sendEvent('Error', 'User Input', error) 429 | // most likely the user canceled the input 430 | } else { 431 | ownerId = value 432 | Settings.setSessionVariable('recentTermVideo', ownerId) 433 | 434 | getData('video.get', { 435 | 'owner_id': ownerId, 436 | 'count': selection, 437 | 'access_token': ACCESS_TOKEN, 438 | 'v': API_VERSION 439 | }) 440 | .then(body => { 441 | let dataKey = context.data.key 442 | let items = util.toArray(context.data.items).map(sketch.fromNative) 443 | items.forEach((item, index) => { 444 | if (!item.type) { 445 | item = sketch.Shape.fromNative(item.sketchObject) 446 | } 447 | 448 | let count = Object.keys(body.response['items'][index].image).length 449 | count = count - 1 450 | process(body.response['items'][index].image[count].url, dataKey, index, item) 451 | 452 | sendEvent('Video', 'Thumbnails', null) 453 | }) 454 | }) 455 | .catch(error => { 456 | UI.message('Something went wrong') 457 | console.error(error) 458 | sendEvent('Error', 'Video', 'Thumbnails: ' + error) 459 | }) 460 | } 461 | } 462 | ) 463 | } 464 | 465 | export function onVideoTitleByOwnerID (context) { 466 | let selection = context.data.requestedCount 467 | let recentTerm = Settings.sessionVariable('recentTermVideo') 468 | let ownerId = (recentTerm === undefined || recentTerm.length === 0) ? USER_ID : recentTerm 469 | UI.getInputFromUser( 470 | 'Enter Video Author ID of vk.com', 471 | { initialValue: ownerId }, 472 | (error, value) => { 473 | if (error || value.length === 0) { 474 | // UI.message(error) 475 | // sendEvent('Error', 'User Input', error) 476 | // most likely the user canceled the input 477 | } else { 478 | ownerId = value 479 | Settings.setSessionVariable('recentTermVideo', ownerId) 480 | 481 | getData('video.get', { 482 | 'owner_id': ownerId, 483 | 'count': selection, 484 | 'access_token': ACCESS_TOKEN, 485 | 'v': API_VERSION 486 | }) 487 | .then(body => { 488 | let dataKey = context.data.key 489 | let items = util.toArray(context.data.items).map(sketch.fromNative) 490 | items.forEach((item, index) => { 491 | if (!item.type) { 492 | item = sketch.Shape.fromNative(item.sketchObject) 493 | } 494 | 495 | if (selection > body.response['items'].length) { 496 | let diff = selection - body.response['items'].length 497 | for (let i = 0; i < diff; i++) { 498 | body.response['items'].push(body.response['items'][i]) 499 | } 500 | } 501 | 502 | let name = body.response['items'][index].title 503 | DataSupplier.supplyDataAtIndex(dataKey, name, index) 504 | 505 | sendEvent('Video', 'Title', null) 506 | }) 507 | }) 508 | .catch(error => { 509 | UI.message('Something went wrong') 510 | console.error(error) 511 | sendEvent('Error', 'Video', 'Title: ' + error) 512 | }) 513 | } 514 | } 515 | ) 516 | } 517 | 518 | export function onVideoViewsByOwnerID (context) { 519 | let selection = context.data.requestedCount 520 | let recentTerm = Settings.sessionVariable('recentTermVideo') 521 | let ownerId = (recentTerm === undefined || recentTerm.length === 0) ? USER_ID : recentTerm 522 | UI.getInputFromUser( 523 | 'Enter Video Author ID of vk.com', 524 | { initialValue: ownerId }, 525 | (error, value) => { 526 | if (error || value.length === 0) { 527 | // UI.message(error) 528 | // sendEvent('Error', 'User Input', error) 529 | // most likely the user canceled the input 530 | } else { 531 | ownerId = value 532 | Settings.setSessionVariable('recentTermVideo', ownerId) 533 | 534 | getData('video.get', { 535 | 'owner_id': ownerId, 536 | 'count': selection, 537 | 'access_token': ACCESS_TOKEN, 538 | 'v': API_VERSION 539 | }) 540 | .then(body => { 541 | let dataKey = context.data.key 542 | let items = util.toArray(context.data.items).map(sketch.fromNative) 543 | items.forEach((item, index) => { 544 | if (!item.type) { 545 | item = sketch.Shape.fromNative(item.sketchObject) 546 | } 547 | 548 | let views = body.response['items'][index].views 549 | views = views + ' ' + getNoun(views, 'просмотр', 'просмотра', 'просмотров') 550 | DataSupplier.supplyDataAtIndex(dataKey, views, index) 551 | 552 | sendEvent('Video', 'Views', null) 553 | }) 554 | }) 555 | .catch(error => { 556 | UI.message('Something went wrong') 557 | console.error(error) 558 | sendEvent('Error', 'Video', 'Video: ' + error) 559 | }) 560 | } 561 | } 562 | ) 563 | } 564 | 565 | export function onMyFriendsRandom (context) { 566 | let selection = context.data.requestedCount 567 | 568 | if ((Settings.sessionVariable('RandomID') === undefined) || (Settings.sessionVariable('RandomType') === 'Image')) { 569 | getData('friends.get', { 570 | 'user_id': USER_ID, 571 | 'order': 'random', 572 | 'fields': 'photo_200,photo_100', 573 | 'access_token': ACCESS_TOKEN, 574 | 'count': selection, 575 | 'v': API_VERSION 576 | }) 577 | .then(body => { 578 | let dataKey = context.data.key 579 | let items = util.toArray(context.data.items).map(sketch.fromNative) 580 | 581 | let arr = [] 582 | for (let i = 0; i < selection; i++) { 583 | arr.splice(i, 0, String(body.response['items'][i].id)) 584 | } 585 | Settings.setSessionVariable('RandomID', arr) 586 | Settings.setSessionVariable('RandomType', 'Image') 587 | items.forEach((item, index) => { 588 | if (!item.type) { 589 | item = sketch.Shape.fromNative(item.sketchObject) 590 | } 591 | 592 | if (!isEmpty(body.response['items'][index].photo_200)) { 593 | process(body.response['items'][index].photo_200, dataKey, index, item) 594 | } else { 595 | process(body.response['items'][index].photo_100, dataKey, index, item) 596 | } 597 | UI.message('Now you can add names in Friends: Random') 598 | 599 | sendEvent('Friends', 'Random', null) 600 | }) 601 | }) 602 | .catch(error => { 603 | UI.message('Something went wrong') 604 | console.error(error) 605 | sendEvent('Error', 'Friends', 'Random, No Cookies: ') 606 | }) 607 | } else { 608 | let userids = Settings.sessionVariable('RandomID').join(',') 609 | getData('users.get', { 610 | 'user_ids': userids, 611 | 'fields': 'photo_200,photo_100', 612 | 'access_token': ACCESS_TOKEN, 613 | 'v': API_VERSION 614 | }) 615 | .then(body => { 616 | let dataKey = context.data.key 617 | let items = util.toArray(context.data.items).map(sketch.fromNative) 618 | items.forEach((item, index) => { 619 | if (!item.type) { 620 | item = sketch.Shape.fromNative(item.sketchObject) 621 | } 622 | 623 | if (!isEmpty(body.response[index].photo_200) !== 0) { 624 | process(body.response[index].photo_200, dataKey, index, item) 625 | } else { 626 | process(body.response[index].photo_100, dataKey, index, item) 627 | } 628 | 629 | sendEvent('Friends', 'Random', null) 630 | }) 631 | }) 632 | .catch(error => { 633 | UI.message('Something went wrong') 634 | console.error(error) 635 | sendEvent('Error', 'Friends', 'Random, Cookies: ' + error) 636 | }) 637 | Settings.setSessionVariable('RandomID', undefined) 638 | } 639 | } 640 | 641 | export function onMyFriendsNamesRandom (context) { 642 | let selection = context.data.requestedCount 643 | 644 | if ((Settings.sessionVariable('RandomID') === undefined) || (Settings.sessionVariable('RandomType') === 'Text')) { 645 | getData('friends.get', { 646 | 'user_id': USER_ID, 647 | 'order': 'random', 648 | 'fields': 'first_name,last_name', 649 | 'access_token': ACCESS_TOKEN, 650 | 'count': selection, 651 | 'v': API_VERSION 652 | }) 653 | .then(body => { 654 | let dataKey = context.data.key 655 | let items = util.toArray(context.data.items).map(sketch.fromNative) 656 | 657 | let arr = [] 658 | for (let i = 0; i < selection; i++) { 659 | arr.splice(i, 0, String(body.response['items'][i].id)) 660 | } 661 | Settings.setSessionVariable('RandomID', arr) 662 | Settings.setSessionVariable('RandomType', 'Text') 663 | 664 | items.forEach((item, index) => { 665 | if (!item.type) { 666 | item = sketch.Shape.fromNative(item.sketchObject) 667 | } 668 | 669 | let fullName = body.response['items'][index].first_name + ' ' + body.response['items'][index].last_name 670 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 671 | UI.message('Now you can add avatars in Friends: Random') 672 | 673 | sendEvent('Friends', 'Random Names', null) 674 | }) 675 | }) 676 | .catch(error => { 677 | UI.message('Something went wrong') 678 | console.error(error) 679 | sendEvent('Error', 'Friends', 'Random Names, No Cookies' + error) 680 | }) 681 | } else { 682 | let userids = Settings.sessionVariable('RandomID').join(',') 683 | getData('users.get', { 684 | 'user_ids': userids, 685 | 'fields': 'first_name,last_name', 686 | 'access_token': ACCESS_TOKEN, 687 | 'v': API_VERSION 688 | }) 689 | .then(body => { 690 | let dataKey = context.data.key 691 | let items = util.toArray(context.data.items).map(sketch.fromNative) 692 | items.forEach((item, index) => { 693 | if (!item.type) { 694 | item = sketch.Shape.fromNative(item.sketchObject) 695 | } 696 | 697 | let fullName = body.response[index].first_name + ' ' + body.response[index].last_name 698 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 699 | }) 700 | 701 | sendEvent('Friends', 'Random Names', null) 702 | }) 703 | .catch(error => { 704 | UI.message('Something went wrong') 705 | console.error(error) 706 | sendEvent('Error', 'Friends', 'Random Names, Cookies' + error) 707 | }) 708 | Settings.setSessionVariable('RandomID', undefined) 709 | } 710 | } 711 | 712 | function GroupsRandom (context, array) { 713 | getData('groups.getById', { 714 | 'group_ids': array, 715 | 'access_token': ACCESS_TOKEN, 716 | 'v': API_VERSION 717 | }) 718 | .then(body => { 719 | let dataKey = context.data.key 720 | let items = util.toArray(context.data.items).map(sketch.fromNative) 721 | 722 | items.forEach((item, index) => { 723 | if (!item.type) { 724 | item = sketch.Shape.fromNative(item.sketchObject) 725 | } 726 | 727 | if (!isEmpty(body.response[index].photo_200)) { 728 | process(body.response[index].photo_200, dataKey, index, item) 729 | } else { 730 | process(body.response[index].photo_100, dataKey, index, item) 731 | } 732 | sendEvent('Groups', 'Random', null) 733 | }) 734 | }) 735 | .catch(error => { 736 | UI.message('Something went wrong') 737 | console.error(error) 738 | sendEvent('Error', 'Groups', 'Random: ' + error) 739 | }) 740 | } 741 | 742 | export function onMyGroupsRandom (context) { 743 | let selection = context.data.requestedCount 744 | 745 | if ((Settings.sessionVariable('RandomGroupsID') === undefined) || (Settings.sessionVariable('RandomGroupsType') === 'Image')) { 746 | getData('groups.get', { 747 | 'user_id': USER_ID, 748 | 'access_token': ACCESS_TOKEN, 749 | 'v': API_VERSION 750 | }) 751 | .then(body => { 752 | let arr = body.response['items'] 753 | arr = shuffle(arr) 754 | 755 | let arrRand = [] 756 | for (let i = 0; i < selection; i++) { 757 | arrRand.splice(i, 0, String(arr[i])) 758 | } 759 | 760 | GroupsRandom(context, arrRand) 761 | UI.message('Now you can add names in Groups: Random') 762 | Settings.setSessionVariable('RandomGroupsID', arrRand) 763 | Settings.setSessionVariable('RandomGroupsType', 'Image') 764 | }) 765 | .catch(error => { 766 | UI.message('Something went wrong') 767 | console.error(error) 768 | sendEvent('Error', 'Groups', 'Random 2: ' + error) 769 | }) 770 | } else { 771 | let ids = Settings.sessionVariable('RandomGroupsID') 772 | GroupsRandom(context, ids) 773 | Settings.setSessionVariable('RandomGroupsID', undefined) 774 | } 775 | } 776 | 777 | function GroupsNamesRandom (context, array) { 778 | getData('groups.getById', { 779 | 'group_ids': array, 780 | 'access_token': ACCESS_TOKEN, 781 | 'v': API_VERSION 782 | }) 783 | .then(body => { 784 | let dataKey = context.data.key 785 | let items = util.toArray(context.data.items).map(sketch.fromNative) 786 | 787 | items.forEach((item, index) => { 788 | if (!item.type) { 789 | item = sketch.Shape.fromNative(item.sketchObject) 790 | } 791 | 792 | let name = body.response[index].name 793 | DataSupplier.supplyDataAtIndex(dataKey, name, index) 794 | sendEvent('Groups', 'Random Names', null) 795 | }) 796 | }) 797 | .catch(error => { 798 | UI.message('Something went wrong') 799 | console.error(error) 800 | sendEvent('Error', 'Groups', 'Random Names: ' + error) 801 | }) 802 | } 803 | 804 | export function onMyGroupsNamesRandom (context) { 805 | let selection = context.data.requestedCount 806 | 807 | if ((Settings.sessionVariable('RandomGroupsID') === undefined) || (Settings.sessionVariable('RandomGroupsType') === 'Text')) { 808 | getData('groups.get', { 809 | 'user_id': USER_ID, 810 | 'access_token': ACCESS_TOKEN, 811 | 'v': API_VERSION 812 | }) 813 | .then(body => { 814 | let arr = body.response['items'] 815 | arr = shuffle(arr) 816 | 817 | let arrRand = [] 818 | for (let i = 0; i < selection; i++) { 819 | arrRand.splice(i, 0, String(arr[i])) 820 | } 821 | 822 | GroupsNamesRandom(context, arrRand) 823 | UI.message('Now you can add avatars in Groups: Random') 824 | Settings.setSessionVariable('RandomGroupsID', arrRand) 825 | Settings.setSessionVariable('RandomGroupsType', 'Text') 826 | }) 827 | .catch(error => { 828 | UI.message('Something went wrong') 829 | console.error(error) 830 | sendEvent('Error', 'Groups', 'Random Names 2: ' + error) 831 | }) 832 | } else { 833 | let ids = Settings.sessionVariable('RandomGroupsID') 834 | GroupsNamesRandom(context, ids) 835 | Settings.setSessionVariable('RandomGroupsID', undefined) 836 | } 837 | } 838 | 839 | export function onBookmarksUsers (context) { 840 | let selection = context.data.requestedCount 841 | getData('fave.getPages', { 842 | 'type': 'users', 843 | 'access_token': ACCESS_TOKEN, 844 | 'fields': 'photo_200,photo_100', 845 | 'count': selection, 846 | 'v': API_VERSION 847 | }) 848 | .then(body => { 849 | let dataKey = context.data.key 850 | let items = util.toArray(context.data.items).map(sketch.fromNative) 851 | items.forEach((item, index) => { 852 | if (!item.type) item = sketch.Shape.fromNative(item.sketchObject) 853 | 854 | if (!isEmpty(body.response['items'][index]['user'].photo_200)) { 855 | process(body.response['items'][index]['user'].photo_200, dataKey, index, item) 856 | } else { 857 | process(body.response['items'][index]['user'].photo_100, dataKey, index, item) 858 | } 859 | 860 | sendEvent('Bookmarks', 'Users', null) 861 | }) 862 | }) 863 | .catch(error => { 864 | UI.message('Something went wrong') 865 | console.error(error) 866 | sendEvent('Error', 'Bookmarks', 'Users: ' + error) 867 | }) 868 | } 869 | 870 | export function onBookmarksUsersNames (context) { 871 | let selection = context.data.requestedCount 872 | getData('fave.getPages', { 873 | 'type': 'users', 874 | 'access_token': ACCESS_TOKEN, 875 | 'fields': 'photo_200,photo_100', 876 | 'count': selection, 877 | 'v': API_VERSION 878 | }) 879 | .then(body => { 880 | let dataKey = context.data.key 881 | let items = util.toArray(context.data.items).map(sketch.fromNative) 882 | items.forEach((item, index) => { 883 | if (!item.type) item = sketch.Shape.fromNative(item.sketchObject) 884 | let fullName = body.response['items'][index]['user'].first_name + ' ' + body.response['items'][index]['user'].last_name 885 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 886 | sendEvent('Bookmarks', 'Users Names', null) 887 | }) 888 | }) 889 | .catch(error => { 890 | UI.message('Something went wrong') 891 | console.error(error) 892 | sendEvent('Error', 'Bookmarks', 'Users Names: ' + error) 893 | }) 894 | } 895 | 896 | export function onBookmarksGroups (context) { 897 | let selection = context.data.requestedCount 898 | getData('fave.getPages', { 899 | 'type': 'groups', 900 | 'access_token': ACCESS_TOKEN, 901 | 'fields': 'photo_200,photo_100', 902 | 'count': selection, 903 | 'v': API_VERSION 904 | }) 905 | .then(body => { 906 | let dataKey = context.data.key 907 | let items = util.toArray(context.data.items).map(sketch.fromNative) 908 | items.forEach((item, index) => { 909 | if (!item.type) item = sketch.Shape.fromNative(item.sketchObject) 910 | 911 | if (!isEmpty(body.response['items'][index]['group'].photo_200)) { 912 | process(body.response['items'][index]['group'].photo_200, dataKey, index, item) 913 | } else { 914 | process(body.response['items'][index]['group'].photo_100, dataKey, index, item) 915 | } 916 | 917 | sendEvent('Bookmarks', 'Groups', null) 918 | }) 919 | }) 920 | .catch(error => { 921 | UI.message('Something went wrong') 922 | console.error(error) 923 | sendEvent('Error', 'Bookmarks', 'Groups: ' + error) 924 | }) 925 | } 926 | 927 | export function onBookmarksGroupsNames (context) { 928 | let selection = context.data.requestedCount 929 | getData('fave.getPages', { 930 | 'type': 'groups', 931 | 'access_token': ACCESS_TOKEN, 932 | 'fields': 'photo_200,photo_100', 933 | 'count': selection, 934 | 'v': API_VERSION 935 | }) 936 | .then(body => { 937 | let dataKey = context.data.key 938 | let items = util.toArray(context.data.items).map(sketch.fromNative) 939 | items.forEach((item, index) => { 940 | if (!item.type) item = sketch.Shape.fromNative(item.sketchObject) 941 | let fullName = body.response['items'][index]['group'].name 942 | DataSupplier.supplyDataAtIndex(dataKey, fullName, index) 943 | sendEvent('Bookmarks', 'Groups Names', null) 944 | }) 945 | }) 946 | .catch(error => { 947 | UI.message('Something went wrong') 948 | console.error(error) 949 | sendEvent('Error', 'Bookmarks', 'Groups Names: ' + error) 950 | }) 951 | } 952 | 953 | export function getData (method, options) { 954 | let esc = encodeURIComponent 955 | let query = Object.keys(options) 956 | .map(key => esc(key) + '=' + esc(options[key])) 957 | .join('&') 958 | 959 | let url = API_URI + method + '?' + query 960 | 961 | if (ACCESS_TOKEN === undefined || Settings.settingForKey('SCOPE_KEY') !== SCOPE) { 962 | auth() 963 | } else { 964 | return new Promise(function (resolve, reject) { 965 | fetch(url) 966 | .then(response => response.json()) 967 | .then(body => { 968 | if(DEBUG_MODE) { 969 | console.log(url) 970 | console.log(USER_ID) 971 | console.log(ACCESS_TOKEN) 972 | console.log(Settings.settingForKey('SCOPE_KEY')) 973 | console.log(body) 974 | } 975 | 976 | if(body.error) { 977 | if(body.error.error_code === 5) logout() 978 | sendEvent('Error', 'API_ERROR', body.error.error_msg) 979 | UI.message(body.error.error_msg) 980 | } 981 | resolve(body) 982 | }) 983 | .catch(e => { 984 | if(DEBUG_MODE) console.error(e) 985 | sendEvent('Error', 'getData', e) 986 | resolve(e) 987 | }) 988 | }) 989 | } 990 | } 991 | 992 | function isEmpty (obj) { 993 | for (var key in obj) { 994 | return false 995 | } 996 | return true 997 | } 998 | 999 | function shuffle (array) { 1000 | let currentIndex = array.length 1001 | let temporaryValue, 1002 | randomIndex 1003 | 1004 | while (currentIndex !== 0) { 1005 | randomIndex = Math.floor(Math.random() * currentIndex) 1006 | currentIndex -= 1 1007 | 1008 | temporaryValue = array[currentIndex] 1009 | array[currentIndex] = array[randomIndex] 1010 | array[randomIndex] = temporaryValue 1011 | } 1012 | 1013 | return array 1014 | } 1015 | 1016 | function process (data, dataKey, index, item) { 1017 | return getImageFromURL(data).then(imagePath => { 1018 | if (!imagePath) { 1019 | UI.message('Something wrong happened') 1020 | sendEvent('Error', 'Main', 'Process') 1021 | return 1022 | } 1023 | 1024 | DataSupplier.supplyDataAtIndex(dataKey, imagePath, index) 1025 | if (item.type !== 'DataOverride') { 1026 | Settings.setLayerSettingForKey(item, SETTING_KEY, data) 1027 | } 1028 | 1029 | let downloadLocation = data 1030 | return fetch(downloadLocation) 1031 | }) 1032 | } 1033 | 1034 | function getImageFromURL (url) { 1035 | return fetch(url) 1036 | .then(res => res.blob()) 1037 | .then(saveTempFileFromImageData) 1038 | .catch((err) => { 1039 | console.error(err) 1040 | sendEvent('Error', 'Main', 'getImageFromURL: ' + err) 1041 | return context.plugin.urlForResourceNamed('placeholder.png').path() 1042 | }) 1043 | } 1044 | 1045 | function saveTempFileFromImageData (imageData) { 1046 | const guid = NSProcessInfo.processInfo().globallyUniqueString() 1047 | const imagePath = path.join(FOLDER, `${guid}.jpg`) 1048 | 1049 | try { 1050 | if (!fs.existsSync(FOLDER)){ 1051 | fs.mkdirSync(FOLDER); 1052 | } 1053 | } catch (error) { 1054 | if(DEBUG_MODE) console.error(error) 1055 | sendEvent('Error', 'Main', 'SaveTempFileFromImageData: ' + error) 1056 | } 1057 | 1058 | try { 1059 | fs.writeFileSync(imagePath, imageData, 'NSData') 1060 | return imagePath 1061 | } catch (error) { 1062 | if(DEBUG_MODE) console.error(error) 1063 | sendEvent('Error', 'Main', 'ImagePath: ' + error) 1064 | return undefined 1065 | } 1066 | } 1067 | 1068 | function getNoun(number, one, two, five) { 1069 | let n = Math.abs(number); 1070 | n %= 100; 1071 | if (n >= 5 && n <= 20) return five; 1072 | 1073 | n %= 10; 1074 | if (n === 1) return one 1075 | 1076 | if (n >= 2 && n <= 4) return two; 1077 | 1078 | return five; 1079 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "💾 VK Data", 3 | "compatibleVersion": 3, 4 | "bundleVersion": 1, 5 | "icon": "./icon.png", 6 | "suppliesData" : true, 7 | "commands": [ 8 | { 9 | "name": "VK Auth", 10 | "identifier": "vk.auth", 11 | "script": "./common.js", 12 | "handler": "checkauth", 13 | "icon": "./icon64.png", 14 | "iconDark": ".icon64.png" 15 | }, 16 | { 17 | "name": "Logout", 18 | "identifier": "vk.logout", 19 | "script": "./common.js", 20 | "handler": "logout", 21 | "icon": "./icon64.png", 22 | "iconDark": ".icon64.png" 23 | }, 24 | { 25 | "script" : "./common.js", 26 | "handlers" : { 27 | "actions" : { 28 | "Startup": "onStartup", 29 | "Shutdown": "onShutdown", 30 | "PhotoByUserID": "onPhotoByUserID", 31 | "MyFriends": "onMyFriends", 32 | "MyFriendsRandom": "onMyFriendsRandom", 33 | "MyGroups": "onMyGroups", 34 | "MyFriendsFirstNames": "onMyFriendsFirstNames", 35 | "MyFriendsLastNames": "onMyFriendsLastNames", 36 | "MyFriendsFullNames": "onMyFriendsFullNames", 37 | "MyGroupsNames": "onMyGroupsNames", 38 | "MyName": "onMyName", 39 | "VideoTitleByOwnerID": "onVideoTitleByOwnerID", 40 | "VideoByOwnerID": "onVideoByOwnerID", 41 | "VideoViewsByOwnerID": "onVideoViewsByOwnerID", 42 | "MyFriendsNamesRandom": "onMyFriendsNamesRandom", 43 | "MyGroupsRandom": "onMyGroupsRandom", 44 | "MyGroupsNamesRandom": "onMyGroupsNamesRandom", 45 | "BookmarksUsers": "onBookmarksUsers", 46 | "BookmarksUsersNames": "onBookmarksUsersNames", 47 | "BookmarksGroups": "onBookmarksGroups", 48 | "BookmarksGroupsNames": "onBookmarksGroupsNames" 49 | } 50 | }, 51 | "icon": "./assets/icon64.png", 52 | "iconDark": "./assets/icon64.png" 53 | } 54 | ], 55 | "menu": { 56 | "items": [ 57 | "vk.auth", 58 | "-", 59 | "vk.logout" 60 | ] 61 | } 62 | } --------------------------------------------------------------------------------