├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── config.go ├── go.mod ├── go.sum ├── main.go ├── my.go ├── screenshots └── qiita-workflow.png ├── search.go ├── setup.go ├── stocks.go └── workflow ├── README.md ├── bin └── alfred-qiita-workflow ├── icon.png └── info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | *.alfredworkflow 3 | 4 | # Created by https://www.gitignore.io/api/go 5 | # Edit at https://www.gitignore.io/?templates=go 6 | 7 | ### Go ### 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.exe~ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Test binary, built with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | ### Go Patch ### 22 | /vendor/ 23 | /Godeps/ 24 | 25 | # End of https://www.gitignore.io/api/go 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - stable 4 | script: 5 | - go get -v github.com/mitchellh/gox 6 | - gox -osarch="linux/amd64" -output="workflow/bin/alfred-qiita-workflow" -ldflags="-X main.Version=`git describe --tags --match 'v*'`" 7 | - "./workflow/bin/alfred-qiita-workflow --version" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Yasuaki Uechi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ALFRED_WORKFLOW_PATH ?= ~/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows 2 | BUNDLE_ID = io.uechi.alfred-workflow.qiita 3 | PACKAGE_FILE = info.plist 4 | SYMLINK_TARGET = ${ALFRED_WORKFLOW_PATH}/${BUNDLE_ID} 5 | 6 | default: build 7 | 8 | build: 9 | gox -osarch="darwin/amd64" -output="workflow/bin/alfred-qiita-workflow" 10 | 11 | package: build 12 | rm -f Qiita.alfredworkflow 13 | cd workflow; zip -r ../Qiita.zip * 14 | mv Qiita.zip Qiita.alfredworkflow 15 | 16 | link: 17 | ln -sf "$(PWD)/workflow" "${SYMLINK_TARGET}" 18 | 19 | unlink: 20 | rm "${SYMLINK_TARGET}" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alfred Qiita Workflow 2 | 3 | [![Build Status](https://travis-ci.com/uetchy/alfred-qiita-workflow.svg?branch=master)](https://travis-ci.com/uetchy/alfred-qiita-workflow) 4 | 5 | ![alt tag](https://raw.github.com/uetchy/alfred-qiita-workflow/master/screenshots/qiita-workflow.png) 6 | 7 | ## Installation 8 | 9 | Download [latest version of Alfred Qiita Workflow](https://github.com/uetchy/alfred-qiita-workflow/releases/latest) and double-click `Qiita.alfredworkflow` to install it. 10 | 11 | ## Commands 12 | 13 | ### Search articles 14 | 15 | ``` 16 | qiita search 17 | ``` 18 | 19 | ### Setup personal access token 20 | 21 | Get [Personal Access Token](https://qiita.com/settings/tokens/new) which have a scope for _read_qiita_ and put it on: 22 | 23 | ``` 24 | qiita setup 25 | ``` 26 | 27 | ### List and search stocked articles 28 | 29 | You MUST set access token in advance. 30 | 31 | ``` 32 | qiita stocks 33 | ``` 34 | 35 | ### List and search your articles 36 | 37 | You MUST set access token in advance. 38 | 39 | ``` 40 | qiita my 41 | ``` 42 | 43 | ## Contributing 44 | 45 | ### Fork project 46 | 47 | 1. Fork it 48 | 2. Create your feature branch (`git checkout -b my-new-feature`) 49 | 3. Commit your changes (`git commit -am 'Added some feature'`) 50 | 4. Push to the branch (`git push origin my-new-feature`) 51 | 5. Create new Pull Request 52 | 53 | ### Development installation 54 | 55 | Run following commands to link this workflow with Alfred. 56 | 57 | ``` 58 | $ go get github.com/uetchy/alfred-qiita-workflow 59 | $ cd $GOPATH/github.com/uetchy/alfred-qiita-workflow 60 | $ make build 61 | $ make link 62 | ``` 63 | 64 | If you put Alfred settings to another location, you should run following lines: 65 | 66 | ``` 67 | $ ALFRED_WORKFLOW_PATH=/path/to/Alfred.alfredpreferences/workflows make link 68 | ``` 69 | 70 | You can find Alfred preferences path by `mdfind` command: 71 | 72 | ``` 73 | $ mdfind Alfred.alfredpreferences 74 | ``` 75 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/mitchellh/go-homedir" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | type config struct { 13 | AccessToken string `json:"accessToken"` 14 | ID string `json:"id"` 15 | } 16 | 17 | var marshaledConfig config 18 | 19 | func getDefaultConfigPath() string { 20 | homeDir, _ := homedir.Dir() 21 | return filepath.Join(homeDir, "Library/Application Support/Alfred 3/Workflow Data/", bundleID) 22 | } 23 | 24 | func loadConfig() error { 25 | configPath := getDefaultConfigPath() 26 | viper.SetConfigName("config") 27 | viper.AddConfigPath(configPath) 28 | viper.SetConfigType("json") 29 | err := viper.ReadInConfig() 30 | if err != nil { 31 | return err 32 | } 33 | return nil 34 | } 35 | 36 | func saveConfig() error { 37 | configPath := getDefaultConfigPath() 38 | viper.Unmarshal(&marshaledConfig) 39 | 40 | buf, err := json.MarshalIndent(marshaledConfig, "", " ") 41 | if err != nil { 42 | return err 43 | } 44 | 45 | err = os.MkdirAll(configPath, 0755) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | f, err := os.Create(filepath.Join(configPath, "config.json")) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | defer f.Close() 56 | 57 | f.WriteString(string(buf)) 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/uetchy/alfred-qiita-workflow 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/codegangsta/cli v1.20.0 7 | github.com/fsnotify/fsnotify v1.4.7 8 | github.com/golang/protobuf v1.2.0 9 | github.com/google/go-querystring v1.0.0 10 | github.com/hashicorp/hcl v1.0.0 11 | github.com/magiconair/properties v1.8.0 12 | github.com/mitchellh/go-homedir v1.1.0 13 | github.com/mitchellh/mapstructure v1.1.2 14 | github.com/pascalw/go-alfred v0.0.0-20160913054623-16aeb807166c 15 | github.com/pelletier/go-toml v1.2.0 16 | github.com/spf13/afero v1.2.1 17 | github.com/spf13/cast v1.3.0 18 | github.com/spf13/jwalterweatherman v1.0.0 19 | github.com/spf13/pflag v1.0.3 20 | github.com/spf13/viper v1.3.1 21 | github.com/uetchy/go-qiita v0.0.0-20150907011308-0949ba53ab87 22 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd 23 | golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635 24 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a 25 | golang.org/x/text v0.3.0 26 | google.golang.org/appengine v1.4.0 27 | gopkg.in/yaml.v2 v2.2.2 28 | ) 29 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 3 | github.com/codegangsta/cli v1.20.0 h1:iX1FXEgwzd5+XN6wk5cVHOGQj6Q3Dcp20lUeS4lHNTw= 4 | github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= 5 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 6 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 7 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 10 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 11 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 12 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 13 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 14 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 15 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 16 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= 17 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 18 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 19 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 20 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 21 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 22 | github.com/pascalw/go-alfred v0.0.0-20160913054623-16aeb807166c h1:j6+n6AEmqTs6XyfTKVaq4Bzc4vCnjwF0BbchwR7UP+U= 23 | github.com/pascalw/go-alfred v0.0.0-20160913054623-16aeb807166c/go.mod h1:O821yF1VSi86tviHFEO4FN4TUjVt46UUqT6XGEaMYhk= 24 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 25 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 28 | github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= 29 | github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 30 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 31 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 32 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 33 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 34 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 35 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 36 | github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38= 37 | github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 38 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 39 | github.com/uetchy/go-qiita v0.0.0-20150907011308-0949ba53ab87 h1:I1D2sxX4AAHRmPfhHhTPBBOTE5opLGz0xcAzt3AcJF4= 40 | github.com/uetchy/go-qiita v0.0.0-20150907011308-0949ba53ab87/go.mod h1:jhEiH3PDC4S5X9lSy1QQBVjQlc03PtQ10a91ukIQHNs= 41 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 42 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 43 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 44 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 45 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 46 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= 47 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 48 | golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635 h1:dOJmQysgY8iOBECuNp0vlKHWEtfiTnyjisEizRV3/4o= 49 | golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 50 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 51 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 52 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 53 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 54 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 55 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 56 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 57 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 58 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 59 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 60 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/codegangsta/cli" 7 | "github.com/spf13/viper" 8 | "github.com/uetchy/go-qiita/qiita" 9 | "golang.org/x/oauth2" 10 | ) 11 | 12 | const ( 13 | bundleID = "co.randompaper.alfred-qiita-workflow" 14 | version = "2.1.0" 15 | ) 16 | 17 | func newQiitaClient() (*qiita.Client, error) { 18 | loadConfig() 19 | var client *qiita.Client 20 | if accessToken := viper.GetString("accessToken"); accessToken != "" { 21 | ts := oauth2.StaticTokenSource( 22 | &oauth2.Token{AccessToken: accessToken}, 23 | ) 24 | tc := oauth2.NewClient(oauth2.NoContext, ts) 25 | client = qiita.NewClient(tc) 26 | } else { 27 | client = qiita.NewClient(nil) 28 | } 29 | 30 | return client, nil 31 | } 32 | 33 | func main() { 34 | app := cli.NewApp() 35 | app.Name = "alfred-qiita" 36 | app.Version = version 37 | app.Commands = []cli.Command{ 38 | { 39 | Name: "setup", 40 | Action: cmdSetup, 41 | }, 42 | { 43 | Name: "search", 44 | Action: cmdSearch, 45 | }, 46 | { 47 | Name: "stocks", 48 | Action: cmdStocks, 49 | }, 50 | { 51 | Name: "my", 52 | Action: cmdMy, 53 | }, 54 | } 55 | app.Run(os.Args) 56 | } 57 | -------------------------------------------------------------------------------- /my.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/codegangsta/cli" 5 | "github.com/pascalw/go-alfred" 6 | ) 7 | 8 | func cmdMy(c *cli.Context) { 9 | query := c.Args() 10 | client, err := newQiitaClient() 11 | if err != nil { 12 | return 13 | } 14 | items, _, err := client.AuthenticatedUser.Items(nil) 15 | if err != nil { 16 | errRes := alfred.NewResponse() 17 | errRes.AddItem(&alfred.AlfredResponseItem{ 18 | Valid: true, 19 | Title: "Failed to authorize", 20 | Subtitle: "Press 'qiita setup' to setup your personal token", 21 | }) 22 | errRes.Print() 23 | return 24 | } 25 | 26 | alfred.InitTerms(query) 27 | response := alfred.NewResponse() 28 | for _, item := range items { 29 | if !alfred.MatchesTerms(query, item.Title+item.Body) { 30 | continue 31 | } 32 | response.AddItem(&alfred.AlfredResponseItem{ 33 | Valid: true, 34 | Uid: item.Id, 35 | Title: item.Title, 36 | Arg: item.URL, 37 | Subtitle: item.CreatedAt.Format("2006/01/02 15:04:05"), 38 | }) 39 | } 40 | 41 | response.Print() 42 | } 43 | -------------------------------------------------------------------------------- /screenshots/qiita-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/alfred-qiita-workflow/12c99e2e52c211f5ae662f2f79f3a6a7c81c1955/screenshots/qiita-workflow.png -------------------------------------------------------------------------------- /search.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/codegangsta/cli" 9 | "github.com/pascalw/go-alfred" 10 | "github.com/uetchy/go-qiita/qiita" 11 | ) 12 | 13 | func cmdSearch(c *cli.Context) { 14 | query := strings.Join(c.Args(), " ") 15 | client, err := newQiitaClient() 16 | if err != nil { 17 | fmt.Println(err) 18 | os.Exit(1) 19 | } 20 | 21 | items, _, _ := client.Items.List(&qiita.ItemsListOptions{Query: query}) 22 | response := alfred.NewResponse() 23 | for _, item := range items { 24 | response.AddItem(&alfred.AlfredResponseItem{ 25 | Valid: true, 26 | Uid: item.Id, 27 | Title: item.Title, 28 | Arg: item.URL, 29 | Subtitle: item.User.Id + " " + item.CreatedAt.Format("2006/01/02 15:04:05"), 30 | }) 31 | } 32 | 33 | response.Print() 34 | } 35 | -------------------------------------------------------------------------------- /setup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/codegangsta/cli" 8 | "github.com/spf13/viper" 9 | "github.com/uetchy/go-qiita/qiita" 10 | "golang.org/x/oauth2" 11 | ) 12 | 13 | func cmdSetup(c *cli.Context) { 14 | token := c.Args().First() 15 | 16 | ts := oauth2.StaticTokenSource( 17 | &oauth2.Token{AccessToken: token}, 18 | ) 19 | tc := oauth2.NewClient(oauth2.NoContext, ts) 20 | client := qiita.NewClient(tc) 21 | user, err := client.AuthenticatedUser.Show() 22 | if err != nil { 23 | fmt.Println("Auth failed") 24 | os.Exit(1) 25 | } 26 | 27 | loadConfig() 28 | viper.Set("accessToken", token) 29 | viper.Set("id", user.Id) 30 | 31 | err = saveConfig() 32 | if err != nil { 33 | fmt.Println(err) 34 | os.Exit(1) 35 | } 36 | 37 | fmt.Println("Token saved") 38 | } 39 | -------------------------------------------------------------------------------- /stocks.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/codegangsta/cli" 5 | "github.com/pascalw/go-alfred" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func cmdStocks(c *cli.Context) { 10 | query := c.Args() 11 | client, err := newQiitaClient() 12 | if err != nil { 13 | return 14 | } 15 | 16 | items, _, err := client.Users.Stocks(viper.GetString("id"), nil) 17 | if err != nil { 18 | errRes := alfred.NewResponse() 19 | errRes.AddItem(&alfred.AlfredResponseItem{ 20 | Valid: true, 21 | Title: "Failed to authorize", 22 | Subtitle: "Press 'qiita setup' to setup your personal token", 23 | }) 24 | errRes.Print() 25 | return 26 | } 27 | 28 | alfred.InitTerms(query) 29 | response := alfred.NewResponse() 30 | for _, item := range items { 31 | if !alfred.MatchesTerms(query, item.Title+item.Body) { 32 | continue 33 | } 34 | response.AddItem(&alfred.AlfredResponseItem{ 35 | Valid: true, 36 | Uid: item.Id, 37 | Title: item.Title, 38 | Arg: item.URL, 39 | Subtitle: item.User.Id + " " + item.CreatedAt.Format("2006/01/02 15:04:05"), 40 | }) 41 | } 42 | 43 | response.Print() 44 | } 45 | -------------------------------------------------------------------------------- /workflow/README.md: -------------------------------------------------------------------------------- 1 | # Alfred Qiita Workflow 2 | 3 | ## Commands 4 | 5 | ### Search articles 6 | 7 | ``` 8 | qiita search 9 | ``` 10 | 11 | ### Setup personal access token 12 | 13 | Get [Personal Access Token](https://qiita.com/settings/tokens/new) which have a scope for _read_qiita_ and put it on: 14 | 15 | ``` 16 | qiita setup 17 | ``` 18 | 19 | ### List and search stocked articles 20 | 21 | You MUST set access token in advance. 22 | 23 | ``` 24 | qiita stocks 25 | ``` 26 | 27 | ### List and search your articles 28 | 29 | You MUST set access token in advance. 30 | 31 | ``` 32 | qiita my 33 | ``` 34 | -------------------------------------------------------------------------------- /workflow/bin/alfred-qiita-workflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/alfred-qiita-workflow/12c99e2e52c211f5ae662f2f79f3a6a7c81c1955/workflow/bin/alfred-qiita-workflow -------------------------------------------------------------------------------- /workflow/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/alfred-qiita-workflow/12c99e2e52c211f5ae662f2f79f3a6a7c81c1955/workflow/icon.png -------------------------------------------------------------------------------- /workflow/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | io.uechi.alfred-workflow.qiita 7 | category 8 | Internet 9 | connections 10 | 11 | 21557827-8003-42B7-A042-16D4C9278FEC 12 | 13 | 14 | destinationuid 15 | 69080009-C277-4E69-B3E0-4E7122F480FD 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | 2310843B-B161-45E3-85DC-2E6012E5F32B 25 | 26 | 27 | destinationuid 28 | CE609731-0A54-412A-A280-60EF147C38BD 29 | modifiers 30 | 0 31 | modifiersubtext 32 | 33 | vitoclose 34 | 35 | 36 | 37 | 4C2D68C3-274B-4EF1-9B54-49425EA5B062 38 | 39 | 40 | destinationuid 41 | 69080009-C277-4E69-B3E0-4E7122F480FD 42 | modifiers 43 | 0 44 | modifiersubtext 45 | 46 | vitoclose 47 | 48 | 49 | 50 | A09B6C79-947F-42CE-91DD-0F3DFDC78C45 51 | 52 | 53 | destinationuid 54 | 2310843B-B161-45E3-85DC-2E6012E5F32B 55 | modifiers 56 | 0 57 | modifiersubtext 58 | 59 | vitoclose 60 | 61 | 62 | 63 | destinationuid 64 | 0A7A04C3-CE54-4A81-B06F-DEC7C926BE5E 65 | modifiers 66 | 1048576 67 | modifiersubtext 68 | Proceed to Qiita for new token with 'read_qiita' scope 69 | vitoclose 70 | 71 | 72 | 73 | D74C6310-657D-4DDD-99E3-1B8F97A8EA49 74 | 75 | 76 | destinationuid 77 | 69080009-C277-4E69-B3E0-4E7122F480FD 78 | modifiers 79 | 0 80 | modifiersubtext 81 | 82 | vitoclose 83 | 84 | 85 | 86 | 87 | createdby 88 | Yasuaki Uechi 89 | description 90 | Alfred workflow for Qiita 91 | disabled 92 | 93 | name 94 | Qiita 95 | objects 96 | 97 | 98 | config 99 | 100 | lastpathcomponent 101 | 102 | onlyshowifquerypopulated 103 | 104 | removeextension 105 | 106 | text 107 | {query} 108 | title 109 | Qiita 110 | 111 | type 112 | alfred.workflow.output.notification 113 | uid 114 | CE609731-0A54-412A-A280-60EF147C38BD 115 | version 116 | 1 117 | 118 | 119 | config 120 | 121 | argumenttype 122 | 1 123 | keyword 124 | qiita setup 125 | subtext 126 | qiita setup <personal access token> / or press cmd+enter to obtain new token 127 | text 128 | Setup Qiita Workflow 129 | withspace 130 | 131 | 132 | type 133 | alfred.workflow.input.keyword 134 | uid 135 | A09B6C79-947F-42CE-91DD-0F3DFDC78C45 136 | version 137 | 1 138 | 139 | 140 | config 141 | 142 | concurrently 143 | 144 | escaping 145 | 0 146 | script 147 | ./bin/alfred-qiita-workflow setup "{query}" 148 | scriptargtype 149 | 0 150 | scriptfile 151 | 152 | type 153 | 0 154 | 155 | type 156 | alfred.workflow.action.script 157 | uid 158 | 2310843B-B161-45E3-85DC-2E6012E5F32B 159 | version 160 | 2 161 | 162 | 163 | config 164 | 165 | browser 166 | 167 | spaces 168 | 169 | url 170 | https://qiita.com/settings/tokens/new 171 | utf8 172 | 173 | 174 | type 175 | alfred.workflow.action.openurl 176 | uid 177 | 0A7A04C3-CE54-4A81-B06F-DEC7C926BE5E 178 | version 179 | 1 180 | 181 | 182 | config 183 | 184 | alfredfiltersresults 185 | 186 | alfredfiltersresultsmatchmode 187 | 0 188 | argumenttreatemptyqueryasnil 189 | 190 | argumenttrimmode 191 | 0 192 | argumenttype 193 | 0 194 | escaping 195 | 126 196 | keyword 197 | qiita search 198 | queuedelaycustom 199 | 3 200 | queuedelayimmediatelyinitially 201 | 202 | queuedelaymode 203 | 1 204 | queuemode 205 | 2 206 | runningsubtext 207 | Seaching for "{query}" 208 | script 209 | ./bin/alfred-qiita-workflow search "{query}" 210 | scriptargtype 211 | 0 212 | scriptfile 213 | 214 | subtext 215 | Search articles for "{query}" 216 | title 217 | Search Qiita 218 | type 219 | 0 220 | withspace 221 | 222 | 223 | type 224 | alfred.workflow.input.scriptfilter 225 | uid 226 | 21557827-8003-42B7-A042-16D4C9278FEC 227 | version 228 | 3 229 | 230 | 231 | config 232 | 233 | alfredfiltersresults 234 | 235 | alfredfiltersresultsmatchmode 236 | 0 237 | argumenttreatemptyqueryasnil 238 | 239 | argumenttrimmode 240 | 0 241 | argumenttype 242 | 1 243 | escaping 244 | 126 245 | keyword 246 | qiita stocks 247 | queuedelaycustom 248 | 3 249 | queuedelayimmediatelyinitially 250 | 251 | queuedelaymode 252 | 1 253 | queuemode 254 | 2 255 | runningsubtext 256 | Seaching for "{query}" 257 | script 258 | ./bin/alfred-qiita-workflow stocks "{query}" 259 | scriptargtype 260 | 0 261 | scriptfile 262 | 263 | subtext 264 | Search stocks for "{query}" 265 | title 266 | Search Qiita Stocks 267 | type 268 | 0 269 | withspace 270 | 271 | 272 | type 273 | alfred.workflow.input.scriptfilter 274 | uid 275 | 4C2D68C3-274B-4EF1-9B54-49425EA5B062 276 | version 277 | 3 278 | 279 | 280 | config 281 | 282 | browser 283 | 284 | spaces 285 | 286 | url 287 | {query} 288 | utf8 289 | 290 | 291 | type 292 | alfred.workflow.action.openurl 293 | uid 294 | 69080009-C277-4E69-B3E0-4E7122F480FD 295 | version 296 | 1 297 | 298 | 299 | config 300 | 301 | alfredfiltersresults 302 | 303 | alfredfiltersresultsmatchmode 304 | 0 305 | argumenttreatemptyqueryasnil 306 | 307 | argumenttrimmode 308 | 0 309 | argumenttype 310 | 1 311 | escaping 312 | 126 313 | keyword 314 | qiita my 315 | queuedelaycustom 316 | 3 317 | queuedelayimmediatelyinitially 318 | 319 | queuedelaymode 320 | 1 321 | queuemode 322 | 2 323 | runningsubtext 324 | Searching for "{query}" 325 | script 326 | ./bin/alfred-qiita-workflow my "{query}" 327 | scriptargtype 328 | 0 329 | scriptfile 330 | 331 | subtext 332 | Search my articles for "{query}" 333 | title 334 | Search Your Qiita Articles 335 | type 336 | 0 337 | withspace 338 | 339 | 340 | type 341 | alfred.workflow.input.scriptfilter 342 | uid 343 | D74C6310-657D-4DDD-99E3-1B8F97A8EA49 344 | version 345 | 3 346 | 347 | 348 | readme 349 | 350 | uidata 351 | 352 | 0A7A04C3-CE54-4A81-B06F-DEC7C926BE5E 353 | 354 | xpos 355 | 415 356 | ypos 357 | 170 358 | 359 | 21557827-8003-42B7-A042-16D4C9278FEC 360 | 361 | xpos 362 | 45 363 | ypos 364 | 170 365 | 366 | 2310843B-B161-45E3-85DC-2E6012E5F32B 367 | 368 | xpos 369 | 415 370 | ypos 371 | 40 372 | 373 | 4C2D68C3-274B-4EF1-9B54-49425EA5B062 374 | 375 | xpos 376 | 45 377 | ypos 378 | 300 379 | 380 | 69080009-C277-4E69-B3E0-4E7122F480FD 381 | 382 | xpos 383 | 415 384 | ypos 385 | 300 386 | 387 | A09B6C79-947F-42CE-91DD-0F3DFDC78C45 388 | 389 | xpos 390 | 45 391 | ypos 392 | 40 393 | 394 | CE609731-0A54-412A-A280-60EF147C38BD 395 | 396 | xpos 397 | 575 398 | ypos 399 | 40 400 | 401 | D74C6310-657D-4DDD-99E3-1B8F97A8EA49 402 | 403 | xpos 404 | 45 405 | ypos 406 | 435 407 | 408 | 409 | webaddress 410 | https://github.com/uetchy/alfred-qiita-workflow 411 | 412 | 413 | --------------------------------------------------------------------------------