Use atproto actions with ease in iOS shortcuts
at main 35 kB view raw
1// 2// IntentsAndDescriptions.swift 3// shortcut 4// 5// Created by Bailey Townsend on 6/30/25. 6// 7 8import Foundation 9import SwiftUI 10 11struct IntentParameter: Identifiable { 12 let id: UUID = UUID() 13 let name: LocalizedStringResource 14 let description: LocalizedStringResource? 15 let type: LocalizedStringResource 16} 17 18struct AppEntityResult: Identifiable { 19 let id: UUID = UUID() 20 let name: LocalizedStringResource 21 let description: LocalizedStringResource 22 let parameters: [IntentParameter] 23} 24 25struct DocUrlLink { 26 let text: String 27 let url: URL 28} 29 30struct IntentDoc: Identifiable { 31 let id: UUID = UUID() 32 let icon: String 33 let name: LocalizedStringResource 34 let description: LocalizedStringResource 35 let parameters: [IntentParameter] 36 let result: AppEntityResult? 37 let docUrl: DocUrlLink? 38} 39 40struct Example { 41 let id: UUID = UUID() 42 let title: LocalizedStringResource 43 let icon: String 44 let description: LocalizedStringResource 45 let shortcutActionsUsed: [LocalizedStringResource] 46 let url: URL? 47} 48 49struct IntentsDocumentation { 50 func createARecordDoc() -> IntentDoc { 51 let createARecordIntent = CreateARecordIntent() 52 return IntentDoc( 53 icon: "document.badge.plus", 54 name: CreateARecordIntent.title, 55 description: CreateARecordIntent.description.descriptionText, 56 parameters: [ 57 IntentParameter( 58 name: createARecordIntent.$atIdentifier.title, 59 description: AtIdentifierAppEntity.typeDescription, 60 type: AtIdentifierAppEntity.typeDisplayName, 61 ), 62 IntentParameter( 63 name: createARecordIntent.$collection.title, 64 description: "The collection you want to write to, like app.bsky.feed.post", 65 type: "NSID", 66 ), 67 IntentParameter( 68 name: createARecordIntent.$recordKey.title, 69 description: 70 "The record key for the new record, optional. A TID will be used if not provided", 71 type: "Record Key", 72 ), 73 IntentParameter( 74 name: createARecordIntent.$shouldValidate.title, 75 description: 76 "You will probably not use this unless you are writing known atproto records. i.e. the ones found in the atproto repo.", 77 type: "Boolean", 78 ), 79 IntentParameter( 80 name: createARecordIntent.$record.title, 81 description: 82 "This is most likely a Dictionary Variable, or a JSON file. But it is the supplied atproto record to be created. We add the $type from the type parameter to this record", 83 type: "JSON serializable object", 84 ), 85 ], 86 result: AppEntityResult( 87 name: StrongReferenceAppEntity.typeDisplayName, 88 description: StrongReferenceAppEntity.typeDescription, 89 parameters: [ 90 IntentParameter( 91 name: StrongReferenceAppEntity.init().$recordURI.title, 92 description: "The URI of the record that was just created", 93 type: "at-uri"), 94 95 IntentParameter( 96 name: StrongReferenceAppEntity.init().$recordCID.title, 97 description: "The CID of the version of the record", 98 type: "CID"), 99 ], 100 101 ), 102 docUrl: DocUrlLink( 103 text: "Bluesky's API documentation", 104 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-repo-create-record")!) 105 ) 106 } 107 108 func createATIDDoc() -> IntentDoc { 109 let createATIDIntent = CreateATidIntent() 110 111 return IntentDoc( 112 icon: "clock", name: CreateATidIntent.title, 113 description: CreateATidIntent.description.descriptionText, 114 parameters: [ 115 IntentParameter( 116 name: createATIDIntent.$date.title, 117 description: 118 "Creates a TID from the given datetime, if one is not given uses the current devices datetime. For best results make sure you use \"Date Format: Long\" and \"Time Format: Long\" on the date parameter", 119 type: LocalizedStringResource(stringLiteral: "DateTime")), 120 IntentParameter( 121 name: createATIDIntent.$clockIdentifier.title, 122 description: 123 "(Optional) Used to create the same TID from the same DateTime. Helpful if you have a record you want to update and used as a unique ID for that record", 124 type: LocalizedStringResource(stringLiteral: "Int")), 125 126 ], 127 result: 128 AppEntityResult( 129 name: "TID", 130 description: 131 "A TID (\"timestamp identifier\") is a compact string identifier based on an integer timestamp. They are sortable, appropriate for use in web URLs, and useful as a \"logical clock\" in networked systems. TIDs are currently used in atproto as record keys and for repository commit \"revision\" numbers.", 132 parameters: [] 133 ), 134 docUrl: DocUrlLink( 135 text: "TID Documentation", url: URL(string: "https://atproto.com/specs/tid")!) 136 ) 137 } 138 139 func getALocalAtIdentifierDoc() -> IntentDoc { 140 let getALocalAtIdentifierIntent = GetALocalAtIdentifierIntent() 141 142 return IntentDoc( 143 icon: "person", name: GetALocalAtIdentifierIntent.title, 144 description: GetALocalAtIdentifierIntent.description.descriptionText, 145 parameters: [ 146 IntentParameter( 147 name: getALocalAtIdentifierIntent.$handle.title, 148 description: 149 "The handle of the saved account you would like to retrieve. Ex @alice.bsky.social", 150 type: "String") 151 ], 152 result: AppEntityResult( 153 name: AtIdentifierAppEntity.typeDisplayName, 154 description: AtIdentifierAppEntity.typeDescription?.localizedStringResource ?? "", 155 parameters: []), 156 docUrl: nil) 157 } 158 159 func getARecordDoc() -> IntentDoc { 160 let getARecordIntent = GetARecordIntent() 161 162 return IntentDoc( 163 icon: "arrow.down.document", 164 name: GetARecordIntent.title, 165 description: GetARecordIntent.description.descriptionText, 166 parameters: [ 167 IntentParameter( 168 name: getARecordIntent.$atIdentifier.title, 169 description: "The handle or DID of the repo", 170 type: "at-identifier"), 171 IntentParameter( 172 name: getARecordIntent.$collection.title, 173 description: "The collection you want to write to, like app.bsky.feed.post", 174 type: "NSID"), 175 IntentParameter( 176 name: getARecordIntent.$recordKey.title, 177 description: "The record key for record", 178 type: "Record Key"), 179 IntentParameter( 180 name: getARecordIntent.$cid.title, 181 description: 182 "The CID of the version of the record. If not specified, then return the most recent version", 183 type: "CID"), 184 ], 185 result: AppEntityResult( 186 name: RecordAppEntity.typeDisplayName, 187 description: RecordAppEntity.typeDescription?.localizedStringResource ?? "", 188 parameters: [ 189 IntentParameter( 190 name: "Record's URI", description: "The at-uri of the record", 191 type: "at-uri"), 192 IntentParameter( 193 name: "Record's CID", description: "The CID of the version of the record", 194 type: "CID"), 195 IntentParameter( 196 name: "Record's value", 197 description: 198 "The record it self in JSON format. Can use Get Dictionary Value to get a specific value", 199 type: "JSON"), 200 201 ]), 202 docUrl: DocUrlLink( 203 text: "Bluesky's API documentation", 204 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-repo-get-record")!) 205 ) 206 } 207 208 func makeAPostDoc() -> IntentDoc { 209 let makeAPostIntent = MakeAPostIntent() 210 return IntentDoc( 211 icon: "signpost.right.and.left", name: MakeAPostIntent.title, 212 description: MakeAPostIntent.description.descriptionText, 213 parameters: [ 214 IntentParameter( 215 name: makeAPostIntent.$atIdentifierToUse.title, 216 description: "The saved AT Identifier you'd like make a Bluesky post to", 217 type: AtIdentifierAppEntity.typeDisplayName), 218 IntentParameter( 219 name: makeAPostIntent.$replyTo.title, 220 description: 221 "The at-uri of the post you want to reply to. Can find it in the result of Bluesky Post if you are making a thread", 222 type: "at-uri"), 223 IntentParameter( 224 name: makeAPostIntent.$images.title, 225 description: "Select up to 4 images to upload. Supports .jpeg, .png, and .heic", 226 type: "jpeg, .png, and .heic"), 227 IntentParameter( 228 name: makeAPostIntent.$altText.title, 229 description: 230 "Add up to 4 alt text descriptions for your images. These will be added in the same order as the images", 231 type: "Array of Strings"), 232 IntentParameter( 233 name: makeAPostIntent.$tags.title, 234 description: 235 "Additional hashtags, in addition to any included in post text and facets. Useful if you want to cross post to Flashes with \"flashes-app-ln3348nvl89\"", 236 type: "Array of Strings"), 237 ], 238 result: AppEntityResult( 239 name: StrongReferenceAppEntity.typeDisplayName, 240 description: StrongReferenceAppEntity.typeDescription, 241 parameters: [ 242 IntentParameter( 243 name: StrongReferenceAppEntity.init().$recordURI.title, 244 description: 245 "The URI of the post that was just created. Can use this in \"Reply to Record URI\" to make a thread", 246 type: "at-uri"), 247 248 IntentParameter( 249 name: StrongReferenceAppEntity.init().$recordURI.title, 250 description: "The URI the record is strongly referencing", 251 type: "at-uri"), 252 ], 253 254 ), 255 docUrl: nil) 256 } 257 258 func putARecordDoc() -> IntentDoc { 259 let putARecordDoc = PutARecordIntent() 260 return IntentDoc( 261 icon: "arrow.up.document", name: PutARecordIntent.title, 262 description: PutARecordIntent.description?.descriptionText ?? "", 263 parameters: [ 264 IntentParameter( 265 name: putARecordDoc.$atIdentifier.title, 266 description: AtIdentifierAppEntity.typeDescription, 267 type: AtIdentifierAppEntity.typeDisplayName, 268 ), 269 IntentParameter( 270 name: putARecordDoc.$collection.title, 271 description: "The collection you want to write to, like app.bsky.feed.post", 272 type: "NSID"), 273 IntentParameter( 274 name: putARecordDoc.$recordKey.title, 275 description: 276 "The record key for the new post, optional. A tid will be used if not provided", 277 type: "Record Key"), 278 IntentParameter( 279 name: putARecordDoc.$shouldValidate.title, 280 description: 281 "You will probably not use this unless you are writing known atproto records. i.e. the ones found in the atproto repo", 282 type: "Boolean"), 283 IntentParameter( 284 name: putARecordDoc.$record.title, 285 description: 286 "This is most likely a Dictionary Variable, or a JSON file. But it is the supplied atproto record. We add the $type from the type parameter to this record", 287 type: "JSON serializable object"), 288 ], 289 result: 290 AppEntityResult( 291 name: StrongReferenceAppEntity.typeDisplayName, 292 description: StrongReferenceAppEntity.typeDescription, 293 parameters: [ 294 IntentParameter( 295 name: StrongReferenceAppEntity.init().$recordURI.title, 296 description: "The URI of the record that was just created or updated", 297 type: "at-uri"), 298 299 IntentParameter( 300 name: StrongReferenceAppEntity.init().$recordCID.title, 301 description: "The CID of the version of the record", 302 type: "CID"), 303 ], 304 305 ), 306 307 docUrl: DocUrlLink( 308 text: "Bluesky's API documentation", 309 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-repo-put-record")!) 310 ) 311 } 312 313 func listRecordsDoc() -> IntentDoc { 314 let listRecordsIntent = ListRecordsIntent() 315 return IntentDoc( 316 icon: "list.bullet", 317 name: ListRecordsIntent.title, 318 description: ListRecordsIntent.description.descriptionText, 319 parameters: [ 320 IntentParameter( 321 name: listRecordsIntent.$atIdentifier.title, 322 description: "The handle or DID of the repo", type: "at-identifier"), 323 IntentParameter( 324 name: listRecordsIntent.$collection.title, 325 description: "The collection you want to write to, like app.bsky.feed.post", 326 type: "NSID"), 327 IntentParameter( 328 name: listRecordsIntent.$limit.title, 329 description: 330 "The number of records to return. Between 2 and 100, defaults to 50", 331 type: "Int"), 332 IntentParameter( 333 name: listRecordsIntent.$cursor.title, 334 description: 335 "This can be a Record Key to used as the last seen record top paginate the results", 336 type: "String"), 337 IntentParameter( 338 name: listRecordsIntent.$reverse.title, 339 description: "Flag to reverse the order of the returned records", 340 type: "Boolean"), 341 342 ], 343 result: AppEntityResult( 344 name: ListRecordsIntent.title, 345 description: ListRecordsIntent.description.descriptionText, 346 parameters: [ 347 IntentParameter( 348 name: ListRecordsAppEntity().$cursor.title, 349 description: "The last record's key to use in pagination", 350 type: "Record Key"), 351 IntentParameter( 352 name: ListRecordsAppEntity().$cursor.title, 353 description: 354 "An array of records. Same type as the Get A Record action. The uri, cid, and the value of the record", 355 type: "JSON serializable object"), 356 357 ]), 358 docUrl: DocUrlLink( 359 text: "Bluesky's API documentation", 360 url: URL( 361 string: "https://docs.bsky.app/docs/api/com-atproto-repo-list-records")!)) 362 } 363 364 func resolveADidOrHandleDoc() -> IntentDoc { 365 366 return IntentDoc( 367 icon: "person.crop.badge.magnifyingglass", name: ResolveDidOrHandleIntent.title, 368 description: ResolveDidOrHandleIntent.description.descriptionText, 369 parameters: [ 370 IntentParameter( 371 name: "DID or Handle", 372 description: 373 "Either the handle if you want to resolve the DID. Or a DID if you want to resolve the handle. If the handle is wrong there is a bit of a delay for a timeout", 374 type: "at-identifier") 375 ], 376 result: AppEntityResult( 377 name: "DID or Handle", 378 description: 379 "Will either return the user's DID if you pass in a handle, or the handle if you pass in a DID", 380 parameters: []), docUrl: nil 381 382 ) 383 384 } 385 386 func deleteARecordDoc() -> IntentDoc { 387 let deleteARecordIntent = DeleteARecordIntent() 388 return IntentDoc( 389 icon: "trash", 390 name: DeleteARecordIntent.title, 391 description: DeleteARecordIntent.description.descriptionText, 392 parameters: [ 393 IntentParameter( 394 name: deleteARecordIntent.$atIdentifier.title, 395 description: AtIdentifierAppEntity.typeDescription, 396 type: AtIdentifierAppEntity.typeDisplayName, 397 ), 398 IntentParameter( 399 name: deleteARecordIntent.$collection.title, 400 description: 401 "The collection you want to delete a record from, like app.bsky.feed.post", 402 type: "NSID", 403 ), 404 IntentParameter( 405 name: deleteARecordIntent.$recordKey.title, 406 description: "The record key for the record you want to delete", 407 type: "Record Key", 408 ), 409 ], 410 result: AppEntityResult( 411 name: "No Result", 412 description: "This action does not return a result", 413 parameters: [], 414 ), 415 docUrl: DocUrlLink( 416 text: "Bluesky's API documentation", 417 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-repo-delete-record")!) 418 ) 419 } 420 421 func createUpdateProfileDoc() -> IntentDoc { 422 let updateProfileIntent = UpdateProfileIntent() 423 return IntentDoc( 424 icon: "person.crop.circle.badge.plus", 425 name: UpdateProfileIntent.title, 426 description: UpdateProfileIntent.description.descriptionText, 427 parameters: [ 428 IntentParameter( 429 name: updateProfileIntent.$atIdentifier.title, 430 description: AtIdentifierAppEntity.typeDescription, 431 type: AtIdentifierAppEntity.typeDisplayName, 432 ), 433 IntentParameter( 434 name: updateProfileIntent.$displayName.title, 435 description: 436 "Enter a value if you would like to update your display name. Leave empty to not update your display name", 437 type: "String", 438 ), 439 IntentParameter( 440 name: updateProfileIntent.$description.title, 441 description: 442 "Enter a value if you would like to update your profile's description. Leave empty to not update your profile's description", 443 type: "String", 444 ), 445 IntentParameter( 446 name: updateProfileIntent.$profilePic.title, 447 description: 448 "Updates your profile picture if provided, if not keeps the current one", 449 type: "jpeg, .png, and .heic", 450 ), 451 IntentParameter( 452 name: updateProfileIntent.$bannerPic.title, 453 description: 454 "Updates your banner picture if provided, if not keeps the current one", 455 type: "jpeg, .png, and .heic", 456 ), 457 ], 458 result: AppEntityResult( 459 name: StrongReferenceAppEntity.typeDisplayName, 460 description: StrongReferenceAppEntity.typeDescription, 461 parameters: [ 462 IntentParameter( 463 name: StrongReferenceAppEntity.init().$recordURI.title, 464 description: "The URI of the profile record that was just updated", 465 type: "at-uri"), 466 467 IntentParameter( 468 name: StrongReferenceAppEntity.init().$recordCID.title, 469 description: "The CID of the version of the updated profile record", 470 type: "CID"), 471 ], 472 473 ), 474 docUrl: nil) 475 } 476 477 func getRepoDoc() -> IntentDoc { 478 let getRepoIntent = GetRepoIntent() 479 return IntentDoc( 480 icon: "arrow.down.circle", 481 name: GetRepoIntent.title, 482 description: GetRepoIntent.description.descriptionText, 483 parameters: [ 484 IntentParameter( 485 name: getRepoIntent.$atIdentifier.title, 486 description: "The handle or DID of the repo", 487 type: "String" 488 ), 489 IntentParameter( 490 name: getRepoIntent.$since.title, 491 description: "A TID created from a timestamp of since when to get the record", 492 type: "String" 493 ), 494 ], 495 result: AppEntityResult( 496 name: "Repo CAR File", 497 description: "Returns a CAR file containing the user's repo data", 498 parameters: [ 499 IntentParameter( 500 name: "CAR File", 501 description: "The downloaded repository as a CAR file.", 502 type: ".CAR File" 503 ) 504 ] 505 ), 506 docUrl: DocUrlLink( 507 text: "Bluesky's API documentation", 508 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-sync-get-repo")!) 509 ) 510 } 511 512 func getListBlobsDoc() -> IntentDoc { 513 let listBlobsIntent = ListBlobsIntent() 514 return IntentDoc( 515 icon: "list.bullet.circle", 516 name: ListBlobsIntent.title, 517 description: ListBlobsIntent.description.descriptionText, 518 parameters: [ 519 IntentParameter( 520 name: listBlobsIntent.$atIdentifier.title, 521 description: "The handle or DID of the repo", 522 type: "String" 523 ), 524 IntentParameter( 525 name: listBlobsIntent.$since.title, 526 description: 527 "A TID created from a timestamp of since when to get the blob's cids", 528 type: "String" 529 ), 530 IntentParameter( 531 name: listBlobsIntent.$limit.title, 532 description: "Limit of blobs to return. Max 1000, default 500", 533 type: "Int" 534 ), 535 IntentParameter( 536 name: listBlobsIntent.$cursor.title, 537 description: "The cursor to paginate through the blobs", 538 type: "String" 539 ), 540 ], 541 result: AppEntityResult( 542 name: "List of Blobs", 543 description: "Returns a list of blob CIDs and cursor for pagination", 544 parameters: [ 545 IntentParameter( 546 name: "CIDs", 547 description: "Array of blob content identifiers", 548 type: "[String]" 549 ), 550 IntentParameter( 551 name: "Cursor", 552 description: "Pagination cursor for next batch of results", 553 type: "String" 554 ), 555 ] 556 ), 557 docUrl: DocUrlLink( 558 text: "Bluesky's API documentation", 559 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-sync-list-blobs")!) 560 ) 561 } 562 563 func getDownloadBlobsDoc() -> IntentDoc { 564 let downloadBlobsIntent = DownloadBlobsIntent() 565 return IntentDoc( 566 icon: "arrow.down.to.line.circle", 567 name: DownloadBlobsIntent.title, 568 description: DownloadBlobsIntent.description.descriptionText, 569 parameters: [ 570 IntentParameter( 571 name: downloadBlobsIntent.$atIdentifier.title, 572 description: "The handle or DID of the repo", 573 type: "String" 574 ), 575 IntentParameter( 576 name: downloadBlobsIntent.$cids.title, 577 description: "The CIDS of the blobs to download", 578 type: "[String]" 579 ), 580 IntentParameter( 581 name: downloadBlobsIntent.$maxConcurrent.title, 582 description: 583 "Number of blobs to download at once. Defaults to 2. This is more of an advance feature, if your downloads are timing out may try increasing this and downloading fewer blobs at once", 584 type: "Int" 585 ), 586 IntentParameter( 587 name: downloadBlobsIntent.$saveLocation.title, 588 description: 589 "Optional save location to speed up downloads if you are just wanting to save to a physical location", 590 type: "IntentFile" 591 ), 592 ], 593 result: AppEntityResult( 594 name: "Downloaded Files", 595 description: "Returns an array of downloaded blob files", 596 parameters: [ 597 IntentParameter( 598 name: "Files", 599 description: "Array of downloaded blob files", 600 type: "[IntentFile]" 601 ) 602 ] 603 ), 604 docUrl: DocUrlLink( 605 text: "Bluesky's API documentation", 606 url: URL(string: "https://docs.bsky.app/docs/api/com-atproto-sync-get-blob")!) 607 ) 608 } 609 610 func getServiceAuthDoc() -> IntentDoc { 611 let getServiceAuthIntent = GetServiceAuthIntent() 612 return IntentDoc( 613 icon: "key.fill", 614 name: GetServiceAuthIntent.title, 615 description: GetServiceAuthIntent.description.descriptionText, 616 parameters: [ 617 IntentParameter( 618 name: getServiceAuthIntent.$atIdentifier.title, 619 description: 620 "The saved AT Identifier of the account you want to use to authenticate with and create a JWT for", 621 type: "AtIdentifierAppEntity" 622 ), 623 IntentParameter( 624 name: getServiceAuthIntent.$lxm.title, 625 description: 626 "This is the XRPC method you want to set in the JWT's claim. like neat.atprotocol.app.privateMessage. Allows for scoping with the external service. Not required", 627 type: "String" 628 ), 629 ], 630 result: AppEntityResult( 631 name: "JWT Token", 632 description: "Returns a signed JWT token for service authentication", 633 parameters: [ 634 IntentParameter( 635 name: "Token", 636 description: "The signed JWT token valid for 60 seconds", 637 type: "String" 638 ) 639 ] 640 ), 641 docUrl: DocUrlLink( 642 text: "Bluesky's API documentation", 643 url: URL( 644 string: "https://docs.bsky.app/docs/api/com-atproto-server-get-service-auth")!) 645 ) 646 } 647 648 // func createUTCDoc() -> IntentDoc { 649 // let utcIntent = UTCIntent() 650 // 651 // return IntentDoc( 652 // icon: "deskclock", 653 // name: UTCIntent.title, 654 // description: UTCIntent.description.descriptionText, 655 // parameters: [ 656 // IntentParameter( 657 // name: utcIntent.$dateSource.title, 658 // description: 659 // "Choose whether to use the current datetime or specify a date. Select 'Current Time' to use the device's current time, or 'Specific Date' to provide a custom datetime.", 660 // type: LocalizedStringResource(stringLiteral: "DateSource")), 661 // IntentParameter( 662 // name: utcIntent.$date.title, 663 // description: 664 // "Creates a UTC Timestamp from the given datetime, if one is not given uses the current devices time to create the timestamp. Make sure you use \"Date Format: Long\" and \"Time Format Long\"", 665 // type: LocalizedStringResource(stringLiteral: "DateTime")), 666 // ], 667 // result: 668 // AppEntityResult( 669 // name: "UTC Timestamp", 670 // description: 671 // "A UTC ISO8601 timestamp string in the format yyyy-MM-dd'T'HH:mm:ss'Z'. This is the recommend format in most lexicons", 672 // parameters: [] 673 // ), 674 // docUrl: DocUrlLink( 675 // text: "Lexicon Documentation On Datetimes", 676 // url: URL(string: "https://atproto.com/specs/lexicon#datetime")!) 677 // ) 678 // } 679 680} 681 682struct Examples { 683 static func getExamples() -> [Example] { 684 [ 685 Example( 686 title: "Create A Bluesky Post", icon: "pencil.and.scribble", 687 description: 688 "Create either a Text or Image post on Bluesky through input prompts.", 689 shortcutActionsUsed: [MakeAPostIntent.title], 690 url: URL( 691 string: "https://www.icloud.com/shortcuts/653b69880e4d443caf02b2d719b4a26e")), 692 693 Example( 694 title: "Statusphere", icon: "globe", 695 description: 696 "[Statusphere](https://atproto.com/guides/applications) is an example of a simple AT Protocol app that the Bluesky team made to help users to get started on building applications on AT Protocol. This is a shortcut version. Choose from a list of emojis to update to your AT Protocol repo on how you are feeling. This example will create a xyz.statusphere.status record in your AT Protocol repo.", 697 shortcutActionsUsed: [CreateARecordIntent.title], 698 url: URL( 699 string: "https://www.icloud.com/shortcuts/fb9dbf682aa24298b6a3cbb967d4040e")), 700 701 Example( 702 title: "Check Someone's Statusphere", icon: "person.crop.badge.magnifyingglass", 703 description: 704 "Check a handle's latest status on Statusphere. This example lists the records found in the xyz.statusphere.status and selects the newest one.", 705 shortcutActionsUsed: [ListRecordsIntent.title], 706 url: URL( 707 string: "https://www.icloud.com/shortcuts/e56a5529215b4416a219923c10cce450")), 708 709 Example( 710 title: "Like A Bluesky Post", icon: "hand.thumbsup", 711 description: 712 "Click share on a Bluesky post and select the \"Like A Bluesky Post\" shortcut to manually create a record and like the post.", 713 shortcutActionsUsed: [GetARecordIntent.title, CreateARecordIntent.title], 714 url: URL( 715 string: "https://www.icloud.com/shortcuts/f33e2b5d87724c239a3b60895818b211")), 716 717 Example( 718 title: "Add User To A List", icon: "list.clipboard", 719 description: 720 "Click share on a Bluesky post and select the \"Add User To A List\" to quickly add a user to a list. This shortcut also shows an example on how to find a saved user's account by their handle.", 721 shortcutActionsUsed: [ 722 GetALocalAtIdentifierIntent.title, ListRecordsIntent.title, 723 CreateARecordIntent.title, 724 ], 725 url: URL( 726 string: "https://www.icloud.com/shortcuts/1fd6b0fe4d464315b3a3f5166795ef68")), 727 728 Example( 729 title: "Update Your Bluesky Profile", icon: "person.crop.circle", 730 description: 731 "Update your Bluesky Profile description to the current song playing on Apple Music. This also updates the banner image to the current album art.", 732 shortcutActionsUsed: [ 733 UpdateProfileIntent.title 734 ], 735 url: URL( 736 string: "https://www.icloud.com/shortcuts/12976af4e00b437593a67838f276cdec")), 737 738 Example( 739 title: "Backup Your Account", icon: "folder.badge.person.crop", 740 description: 741 "Backup your AT Protocol/Bluesky account to your phone. This shortcut downloads a copy of your records as a car file and downloads all your blobs(photos and videos) to a local folder you choose on your phone. May need to play around or run this short multiple times for first download. After each run it's faster since it's only downloading new blobs. The shortcut lists 250 of your blobs 10 times and downloads them 2 at a time, so may need to find settings that suit your network and phone a bit better.", 742 shortcutActionsUsed: [ 743 ResolveDidOrHandleIntent.title, 744 GetRepoIntent.title, 745 ListBlobsIntent.title, 746 DownloadBlobsIntent.title, 747 748 ], 749 url: URL( 750 string: "https://www.icloud.com/shortcuts/7c00fabe8b3e4b4492b20922277d9f0a")), 751 ] 752 } 753 754}