├── life ├── icon.png ├── life.png ├── life.alfredworkflow ├── Makefile ├── life.swift └── info.plist ├── chouti ├── icon.png ├── chouti.png ├── chouti.alfredworkflow ├── Makefile ├── chouti.swift └── info.plist ├── README.md └── .gitignore /life/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/life/icon.png -------------------------------------------------------------------------------- /life/life.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/life/life.png -------------------------------------------------------------------------------- /chouti/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/chouti/icon.png -------------------------------------------------------------------------------- /chouti/chouti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/chouti/chouti.png -------------------------------------------------------------------------------- /life/life.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/life/life.alfredworkflow -------------------------------------------------------------------------------- /chouti/chouti.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CFNull/workflow/HEAD/chouti/chouti.alfredworkflow -------------------------------------------------------------------------------- /life/Makefile: -------------------------------------------------------------------------------- 1 | OUTPUT = life.alfredworkflow 2 | FILES = Info.plist life.swift icon.png 3 | 4 | all: $(OUTPUT) 5 | 6 | clean: 7 | -rm $(OUTPUT) 8 | 9 | $(OUTPUT): $(FILES) 10 | zip $@ $(FILES) 11 | -------------------------------------------------------------------------------- /chouti/Makefile: -------------------------------------------------------------------------------- 1 | OUTPUT = chouti.alfredworkflow 2 | FILES = Info.plist chouti.swift icon.png 3 | 4 | all: $(OUTPUT) 5 | 6 | clean: 7 | -rm $(OUTPUT) 8 | 9 | $(OUTPUT): $(FILES) 10 | zip $@ $(FILES) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Workflow 2 | 3 | 用swift写下Alfred workflow 4 | 5 | ## [人生进度条](https://raw.githubusercontent.com/pdso/workflow/master/life/life.alfredworkflow) 6 | 7 | ### 输入 `life` 查看进度 8 | 9 | ![life](./life/life.png) 10 | 11 | ## [抽屉新热榜](https://raw.githubusercontent.com/pdso/workflow/master/chouti/chouti.alfredworkflow) 12 | 13 | ### 输入 `chouti`看新闻 14 | 15 | ![chouti](./chouti/chouti.png) 16 | -------------------------------------------------------------------------------- /chouti/chouti.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/swift 2 | // chouti 3 | // Created by pqso on 2022/1/6. 4 | // Copyright © 2022 pqso. All rights reserved. 5 | 6 | import Foundation 7 | 8 | struct Response: Codable { 9 | let data: [Entry] 10 | } 11 | 12 | struct Entry: Codable { 13 | let title: String 14 | let originalUrl: String 15 | let sectionImgUrl: String? 16 | let sectionName: String? 17 | } 18 | 19 | let sectionName = "抽屉挂了吗" 20 | 21 | var sema = DispatchSemaphore( value: 0 ) 22 | 23 | if let url = URL(string: "https://m.chouti.com/api/m/link/hot?afterTime=0") { 24 | URLSession.shared.dataTask(with: url) { data, response, error in 25 | var array: [[String : String]] = [] 26 | if let data = data { 27 | let jsonDecoder = JSONDecoder() 28 | do { 29 | let res = try jsonDecoder.decode(Response.self, from: data) 30 | res.data.forEach { entry in 31 | array.append([ 32 | "title": entry.title, 33 | "subtitle": entry.sectionName ?? sectionName, 34 | "arg": entry.originalUrl 35 | ]) 36 | } 37 | 38 | let items = ["items": array] 39 | let resultData = try JSONSerialization.data(withJSONObject: items, options: .prettyPrinted) 40 | print(String(data: resultData, encoding: .utf8)!) 41 | 42 | }catch{ 43 | let items = ["items": ["title": error.localizedDescription]] 44 | print(items) 45 | } 46 | } 47 | sema.signal() 48 | }.resume() 49 | } 50 | 51 | sema.wait() 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /life/life.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/swift 2 | // life 3 | // Created by pqso on 2019/10/9. 4 | // Copyright © 2019 pqso. All rights reserved. 5 | 6 | import Foundation 7 | 8 | func getSubTitle(_ count: Int, total: Int) -> String { 9 | let finished = "■" 10 | let unFinished = "□" 11 | 12 | let percent = Double(count) / Double(total) 13 | let f = lrint(30 * percent) 14 | let uF = 30 - f 15 | 16 | return String(repeating: finished, count: f) + String(repeating: unFinished, count: uF) + " \(lrint(percent * 100))%" 17 | } 18 | 19 | struct Life{ 20 | enum Style: String{ 21 | case hoursInDay = "hoursInDay" 22 | case dayInWeek = "dayInWeek" 23 | case weekInYear = "weekInYear" 24 | case dayInMonth = "dayInMonth" 25 | case monthInYear = "monthInYear" 26 | } 27 | let style: Style 28 | var alfredItem: [String: String] = [:] 29 | 30 | init(_ style: Style) { 31 | let calendar = Calendar.current 32 | let date = Date() 33 | let year = calendar.component(.year, from: date) 34 | 35 | var dic: [String : String] = [:] 36 | switch style { 37 | case .hoursInDay: 38 | let hour = calendar.component(.hour, from:date) 39 | dic = ["title": "今天已经过去了 \(hour) 小时", "subtitle": getSubTitle(hour, total: 23), "arg": "这是一个测试"] 40 | case .dayInWeek: 41 | let day = calendar.component(.weekday, from: date) 42 | dic = ["title": "本周已过去了 \(day - 1) 天", "subtitle": getSubTitle(day - 1, total: 7)] 43 | case .weekInYear: 44 | let weekRange = calendar.range(of: .weekOfYear, in: .yearForWeekOfYear, for: Date())! 45 | let week = calendar.component(.weekOfYear, from: date) 46 | dic = ["title": "\(year) 年已经过去了 \(week - 1) 周", "subtitle": getSubTitle(week - 1, total: weekRange.count)] 47 | case .dayInMonth: 48 | let dayth = calendar.component(.day, from: date) 49 | let monthRange = calendar.range(of: .day, in: .month, for: date)! 50 | dic = ["title": "这个月已经过去了 \(dayth - 1) 天", "subtitle": getSubTitle(dayth - 1, total: monthRange.count)] 51 | case .monthInYear: 52 | let month = calendar.component(.month, from: date) 53 | dic = ["title": "\(year) 年已经过去了 \(month - 1) 个月", "subtitle": getSubTitle(month - 1, total: 12)] 54 | } 55 | self.style = style 56 | dic["arg"] = dic["title"]! + "\n" + dic["subtitle"]! 57 | alfredItem = dic 58 | } 59 | } 60 | 61 | let hoursInDay = Life(.hoursInDay).alfredItem 62 | let dayInWeek = Life(.dayInWeek).alfredItem 63 | let weekInYear = Life(.weekInYear).alfredItem 64 | let dayInMonth = Life(.dayInMonth).alfredItem 65 | let monthInYear = Life(.monthInYear).alfredItem 66 | 67 | let alfredItems = ["items": [hoursInDay, dayInWeek, dayInMonth, weekInYear, monthInYear]] 68 | let resultData = try JSONSerialization.data(withJSONObject: alfredItems, options: .prettyPrinted) 69 | print(String(data: resultData, encoding: .utf8)!) 70 | -------------------------------------------------------------------------------- /chouti/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | pq.swift.chouti 7 | connections 8 | 9 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 10 | 11 | 12 | destinationuid 13 | 33731783-BC44-4EAF-A155-F336BA94879C 14 | modifiers 15 | 0 16 | modifiersubtext 17 | 18 | vitoclose 19 | 20 | 21 | 22 | 23 | createdby 24 | pqso 25 | description 26 | 有点正经的内容社区 27 | disabled 28 | 29 | name 30 | chouti 31 | objects 32 | 33 | 34 | config 35 | 36 | browser 37 | 38 | spaces 39 | 40 | url 41 | {query} 42 | utf8 43 | 44 | 45 | type 46 | alfred.workflow.action.openurl 47 | uid 48 | 33731783-BC44-4EAF-A155-F336BA94879C 49 | version 50 | 1 51 | 52 | 53 | config 54 | 55 | alfredfiltersresults 56 | 57 | alfredfiltersresultsmatchmode 58 | 0 59 | argumenttreatemptyqueryasnil 60 | 61 | argumenttrimmode 62 | 0 63 | argumenttype 64 | 1 65 | escaping 66 | 127 67 | keyword 68 | chouti 69 | queuedelaycustom 70 | 1 71 | queuedelayimmediatelyinitially 72 | 73 | queuedelaymode 74 | 0 75 | queuemode 76 | 1 77 | runningsubtext 78 | 正在努力请求数据 79 | scriptargtype 80 | 1 81 | scriptfile 82 | chouti.swift 83 | subtext 84 | 有点正经有内容社区 85 | title 86 | 抽屉新热榜 87 | type 88 | 8 89 | withspace 90 | 91 | 92 | type 93 | alfred.workflow.input.scriptfilter 94 | uid 95 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 96 | version 97 | 3 98 | 99 | 100 | readme 101 | 抽屉新热榜 102 | uidata 103 | 104 | 33731783-BC44-4EAF-A155-F336BA94879C 105 | 106 | xpos 107 | 240 108 | ypos 109 | 50 110 | 111 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 112 | 113 | note 114 | 抽屉新热榜 115 | xpos 116 | 70 117 | ypos 118 | 50 119 | 120 | 121 | variablesdontexport 122 | 123 | version 124 | 1.0.0 125 | webaddress 126 | https://dig.chouti.com 127 | 128 | 129 | -------------------------------------------------------------------------------- /life/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | pd.swift.life 7 | category 8 | Tools 9 | connections 10 | 11 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 12 | 13 | 14 | destinationuid 15 | 78006BFE-6A14-487D-B2FC-434544A46A7A 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | 78006BFE-6A14-487D-B2FC-434544A46A7A 25 | 26 | 27 | createdby 28 | pdso 29 | description 30 | 人生进度条 31 | disabled 32 | 33 | name 34 | life progress 35 | objects 36 | 37 | 38 | config 39 | 40 | alfredfiltersresults 41 | 42 | alfredfiltersresultsmatchmode 43 | 0 44 | argumenttreatemptyqueryasnil 45 | 46 | argumenttrimmode 47 | 0 48 | argumenttype 49 | 2 50 | escaping 51 | 127 52 | keyword 53 | life 54 | queuedelaycustom 55 | 3 56 | queuedelayimmediatelyinitially 57 | 58 | queuedelaymode 59 | 0 60 | queuemode 61 | 1 62 | runningsubtext 63 | 64 | script 65 | # 66 | # Alfred Script Filter JSON format 67 | # 68 | # This example demonstrates all fields available for populating results. 69 | # 70 | # For an in-depth explanation, use the (?) help button to the bottom left. 71 | # 72 | 73 | cat << EOB 74 | {"items": [ 75 | 76 | { 77 | "uid": "desktop", 78 | "type": "file", 79 | "title": "Desktop", 80 | "subtitle": "~/Desktop", 81 | "arg": "~/Desktop", 82 | "autocomplete": "Desktop", 83 | "icon": { 84 | "type": "fileicon", 85 | "path": "~/Desktop" 86 | } 87 | }, 88 | 89 | { 90 | "valid": false, 91 | "uid": "flickr", 92 | "title": "Flickr", 93 | "icon": { 94 | "path": "flickr.png" 95 | } 96 | }, 97 | 98 | { 99 | "uid": "image", 100 | "type": "file", 101 | "title": "My holiday photo", 102 | "subtitle": "~/Pictures/My holiday photo.jpg", 103 | "autocomplete": "My holiday photo", 104 | "icon": { 105 | "type": "filetype", 106 | "path": "public.jpeg" 107 | } 108 | }, 109 | 110 | { 111 | "valid": false, 112 | "uid": "alfredapp", 113 | "title": "Alfred Website", 114 | "subtitle": "https://www.alfredapp.com/", 115 | "arg": "alfredapp.com", 116 | "autocomplete": "Alfred Website", 117 | "quicklookurl": "https://www.alfredapp.com/", 118 | "mods": { 119 | "alt": { 120 | "valid": true, 121 | "arg": "alfredapp.com/powerpack", 122 | "subtitle": "https://www.alfredapp.com/powerpack/" 123 | }, 124 | "cmd": { 125 | "valid": true, 126 | "arg": "alfredapp.com/powerpack/buy/", 127 | "subtitle": "https://www.alfredapp.com/powerpack/buy/" 128 | }, 129 | }, 130 | "text": { 131 | "copy": "https://www.alfredapp.com/ (text here to copy)", 132 | "largetype": "https://www.alfredapp.com/ (text here for large type)" 133 | } 134 | } 135 | 136 | ]} 137 | EOB 138 | scriptargtype 139 | 1 140 | scriptfile 141 | life.swift 142 | subtext 143 | 144 | title 145 | 人生进度条 146 | type 147 | 8 148 | withspace 149 | 150 | 151 | type 152 | alfred.workflow.input.scriptfilter 153 | uid 154 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 155 | version 156 | 3 157 | 158 | 159 | config 160 | 161 | autopaste 162 | 163 | clipboardtext 164 | {query} 165 | transient 166 | 167 | 168 | type 169 | alfred.workflow.output.clipboard 170 | uid 171 | 78006BFE-6A14-487D-B2FC-434544A46A7A 172 | version 173 | 3 174 | 175 | 176 | readme 177 | 人生进度条 178 | Alfred 3 now supports JSON, which is the recommended format. XML is also supported for legacy Alfred 2 workflows, and has been updated to include the new features. 179 | 180 | A new option now exists for Script Filters, "Alfred filters results". This allows you to return a whole list of results, let Alfred do the subsequent filtering with extreme performance. When this option is selected, your script will be called once with no argument. 181 | uidata 182 | 183 | 50B0CD10-4D52-4898-9C05-DD3A34A829C3 184 | 185 | note 186 | lifeprogress 187 | xpos 188 | 70 189 | ypos 190 | 65 191 | 192 | 78006BFE-6A14-487D-B2FC-434544A46A7A 193 | 194 | xpos 195 | 240 196 | ypos 197 | 65 198 | 199 | 200 | version 201 | 1.0.0 202 | webaddress 203 | https://github.com/pdso/workflow 204 | 205 | 206 | --------------------------------------------------------------------------------