Skip to content

Commit 561c91c

Browse files
author
tiann
committed
init
0 parents  commit 561c91c

File tree

18 files changed

+1399
-0
lines changed

18 files changed

+1399
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
*pyc

clipboard.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- coding: utf-8 -*-
2+
import time
3+
from AppKit import NSPasteboard, NSPasteboardTypePNG, NSPasteboardTypeTIFF, NSPasteboardTypeString
4+
5+
def get_paste_img_file():
6+
pb = NSPasteboard.generalPasteboard()
7+
data_type = pb.types()
8+
# if img file
9+
print data_type
10+
now = int(time.time() * 1000) # used for filename
11+
if NSPasteboardTypePNG in data_type:
12+
# png
13+
data = pb.dataForType_(NSPasteboardTypePNG)
14+
filename = '%s.png' % now
15+
filepath = '/tmp/%s' % filename
16+
ret = data.writeToFile_atomically_(filepath, False)
17+
if ret:
18+
return filepath
19+
elif NSPasteboardTypeTIFF in data_type:
20+
# tiff
21+
data = pb.dataForType_(NSPasteboardTypeTIFF)
22+
filename = '%s.tiff' % now
23+
filepath = '/tmp/%s' % filename
24+
ret = data.writeToFile_atomically_(filepath, False)
25+
if ret:
26+
return filepath
27+
elif NSPasteboardTypeString in data_type:
28+
# string todo, recognise url of png & jpg
29+
pass
30+
31+
32+
33+
34+

info.plist

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>bundleid</key>
6+
<string>com.weishu.markdownimg</string>
7+
<key>category</key>
8+
<string>Productivity</string>
9+
<key>connections</key>
10+
<dict>
11+
<key>8BFF9926-79DA-413A-A4EB-63DB997DF739</key>
12+
<array/>
13+
<key>AFB4A236-0658-4EC5-BA89-1CCF93C2A4A3</key>
14+
<array>
15+
<dict>
16+
<key>destinationuid</key>
17+
<string>8BFF9926-79DA-413A-A4EB-63DB997DF739</string>
18+
<key>modifiers</key>
19+
<integer>0</integer>
20+
<key>modifiersubtext</key>
21+
<string></string>
22+
</dict>
23+
</array>
24+
</dict>
25+
<key>createdby</key>
26+
<string>weishu</string>
27+
<key>description</key>
28+
<string>Utility to simplify the img upload in markdown</string>
29+
<key>disabled</key>
30+
<false/>
31+
<key>name</key>
32+
<string>markdown img</string>
33+
<key>objects</key>
34+
<array>
35+
<dict>
36+
<key>config</key>
37+
<dict>
38+
<key>action</key>
39+
<integer>0</integer>
40+
<key>argument</key>
41+
<integer>0</integer>
42+
<key>hotkey</key>
43+
<integer>9</integer>
44+
<key>hotmod</key>
45+
<integer>1310720</integer>
46+
<key>hotstring</key>
47+
<string>V</string>
48+
<key>leftcursor</key>
49+
<false/>
50+
<key>modsmode</key>
51+
<integer>0</integer>
52+
<key>relatedAppsMode</key>
53+
<integer>0</integer>
54+
</dict>
55+
<key>type</key>
56+
<string>alfred.workflow.trigger.hotkey</string>
57+
<key>uid</key>
58+
<string>AFB4A236-0658-4EC5-BA89-1CCF93C2A4A3</string>
59+
<key>version</key>
60+
<integer>1</integer>
61+
</dict>
62+
<dict>
63+
<key>config</key>
64+
<dict>
65+
<key>concurrently</key>
66+
<false/>
67+
<key>escaping</key>
68+
<integer>68</integer>
69+
<key>script</key>
70+
<string>query = "{query}"
71+
from clipboard import get_paste_img_file
72+
from upload import upload_qiniu
73+
import os
74+
import subprocess
75+
76+
def noti(msg, title="notice"):
77+
os.system('osascript -e \'display notification "%s" with title "%s"\'' % (msg, title))
78+
79+
url = "http://7sbqce.com1.z0.glb.clouddn.com/markdown"
80+
81+
img_file = get_paste_img_file()
82+
if img_file:
83+
# has file
84+
name = os.path.split(img_file)[1]
85+
size_str = subprocess.check_output('sips -g pixelWidth %s | tail -n1 | cut -d" " -f4' % img_file, shell=True)
86+
size = int(size_str.strip()) / 2
87+
markdown_url = '&lt;img src="%s/%s" width="%d"/&gt;' % (url, name, size)
88+
# make it to clipboard
89+
os.system("echo '%s' | pbcopy" % markdown_url)
90+
os.system('osascript -e \'tell application "System Events" to keystroke "v" using command down\'')
91+
ret = upload_qiniu(img_file)
92+
if not ret: noti("upload image to qiniu failed")
93+
else:
94+
noti("there are no image file in clipboard")
95+
</string>
96+
<key>type</key>
97+
<integer>3</integer>
98+
</dict>
99+
<key>type</key>
100+
<string>alfred.workflow.action.script</string>
101+
<key>uid</key>
102+
<string>8BFF9926-79DA-413A-A4EB-63DB997DF739</string>
103+
<key>version</key>
104+
<integer>0</integer>
105+
</dict>
106+
</array>
107+
<key>readme</key>
108+
<string></string>
109+
<key>uidata</key>
110+
<dict>
111+
<key>8BFF9926-79DA-413A-A4EB-63DB997DF739</key>
112+
<dict>
113+
<key>ypos</key>
114+
<real>10</real>
115+
</dict>
116+
<key>AFB4A236-0658-4EC5-BA89-1CCF93C2A4A3</key>
117+
<dict>
118+
<key>ypos</key>
119+
<real>10</real>
120+
</dict>
121+
</dict>
122+
<key>webaddress</key>
123+
<string>http://www.tianweishu.com</string>
124+
</dict>
125+
</plist>

qiniu/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
'''
3+
Qiniu Resource Storage SDK for Python
4+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5+
6+
For detailed document, please see:
7+
<http://developer.qiniu.com>
8+
'''
9+
10+
# flake8: noqa
11+
12+
__version__ = '7.0.5'
13+
14+
from .auth import Auth
15+
16+
from .config import set_default, Zone
17+
18+
from .services.storage.bucket import BucketManager, build_batch_copy, build_batch_rename, build_batch_move, build_batch_stat, build_batch_delete
19+
from .services.storage.uploader import put_data, put_file, put_stream
20+
from .services.processing.pfop import PersistentFop
21+
from .services.processing.cmd import build_op, pipe_cmd, op_save
22+
23+
from .utils import urlsafe_base64_encode, urlsafe_base64_decode, etag, entry

qiniu/auth.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import hmac
4+
import time
5+
from hashlib import sha1
6+
7+
from requests.auth import AuthBase
8+
9+
from .compat import urlparse, json, b
10+
from .utils import urlsafe_base64_encode
11+
12+
13+
# 上传策略,参数规格详见
14+
# http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
15+
_policy_fields = set([
16+
'callbackUrl', # 回调URL
17+
'callbackBody', # 回调Body
18+
'callbackHost', # 回调URL指定的Host
19+
'callbackBodyType', # 回调Body的Content-Type
20+
'callbackFetchKey', # 回调FetchKey模式开关
21+
22+
'returnUrl', # 上传端的303跳转URL
23+
'returnBody', # 上传端简单反馈获取的Body
24+
25+
'endUser', # 回调时上传端标识
26+
'saveKey', # 自定义资源名
27+
'insertOnly', # 插入模式开关
28+
29+
'detectMime', # MimeType侦测开关
30+
'mimeLimit', # MimeType限制
31+
'fsizeLimit', # 上传文件大小限制
32+
33+
'persistentOps', # 持久化处理操作
34+
'persistentNotifyUrl', # 持久化处理结果通知URL
35+
'persistentPipeline', # 持久化处理独享队列
36+
])
37+
38+
_deprecated_policy_fields = set([
39+
'asyncOps'
40+
])
41+
42+
43+
class Auth(object):
44+
"""七牛安全机制类
45+
46+
该类主要内容是七牛上传凭证、下载凭证、管理凭证三种凭证的签名接口的实现,以及回调验证。
47+
48+
Attributes:
49+
__access_key: 账号密钥对中的accessKey,详见 https://portal.qiniu.com/setting/key
50+
__secret_key: 账号密钥对重的secretKey,详见 https://portal.qiniu.com/setting/key
51+
"""
52+
53+
def __init__(self, access_key, secret_key):
54+
"""初始化Auth类"""
55+
self.__checkKey(access_key, secret_key)
56+
self.__access_key = access_key
57+
self.__secret_key = b(secret_key)
58+
59+
def __token(self, data):
60+
data = b(data)
61+
hashed = hmac.new(self.__secret_key, data, sha1)
62+
return urlsafe_base64_encode(hashed.digest())
63+
64+
def token(self, data):
65+
return '{0}:{1}'.format(self.__access_key, self.__token(data))
66+
67+
def token_with_data(self, data):
68+
data = urlsafe_base64_encode(data)
69+
return '{0}:{1}:{2}'.format(self.__access_key, self.__token(data), data)
70+
71+
def token_of_request(self, url, body=None, content_type=None):
72+
"""带请求体的签名(本质上是管理凭证的签名)
73+
74+
Args:
75+
url: 待签名请求的url
76+
body: 待签名请求的body
77+
content_type: 待签名请求的body的Content-Type
78+
79+
Returns:
80+
管理凭证
81+
"""
82+
parsed_url = urlparse(url)
83+
query = parsed_url.query
84+
path = parsed_url.path
85+
data = path
86+
if query != '':
87+
data = ''.join([data, '?', query])
88+
data = ''.join([data, "\n"])
89+
90+
if body:
91+
mimes = [
92+
'application/x-www-form-urlencoded'
93+
]
94+
if content_type in mimes:
95+
data += body
96+
97+
return '{0}:{1}'.format(self.__access_key, self.__token(data))
98+
99+
@staticmethod
100+
def __checkKey(access_key, secret_key):
101+
if not (access_key and secret_key):
102+
raise ValueError('invalid key')
103+
104+
def private_download_url(self, url, expires=3600):
105+
"""生成私有资源下载链接
106+
107+
Args:
108+
url: 私有空间资源的原始URL
109+
expires: 下载凭证有效期,默认为3600s
110+
111+
Returns:
112+
私有资源的下载链接
113+
"""
114+
deadline = int(time.time()) + expires
115+
if '?' in url:
116+
url += '&'
117+
else:
118+
url += '?'
119+
url = '{0}e={1}'.format(url, str(deadline))
120+
121+
token = self.token(url)
122+
return '{0}&token={1}'.format(url, token)
123+
124+
def upload_token(self, bucket, key=None, expires=3600, policy=None, strict_policy=True):
125+
"""生成上传凭证
126+
127+
Args:
128+
bucket: 上传的空间名
129+
key: 上传的文件名,默认为空
130+
expires: 上传凭证的过期时间,默认为3600s
131+
policy: 上传策略,默认为空
132+
133+
Returns:
134+
上传凭证
135+
"""
136+
if bucket is None or bucket == '':
137+
raise ValueError('invalid bucket name')
138+
139+
scope = bucket
140+
if key is not None:
141+
scope = '{0}:{1}'.format(bucket, key)
142+
143+
args = dict(
144+
scope=scope,
145+
deadline=int(time.time()) + expires,
146+
)
147+
148+
if policy is not None:
149+
self.__copy_policy(policy, args, strict_policy)
150+
151+
return self.__upload_token(args)
152+
153+
def __upload_token(self, policy):
154+
data = json.dumps(policy, separators=(',', ':'))
155+
return self.token_with_data(data)
156+
157+
def verify_callback(self, origin_authorization, url, body, content_type='application/x-www-form-urlencoded'):
158+
"""回调验证
159+
160+
Args:
161+
origin_authorization: 回调时请求Header中的Authorization字段
162+
url: 回调请求的url
163+
body: 回调请求的body
164+
content_type: 回调请求body的Content-Type
165+
166+
Returns:
167+
返回true表示验证成功,返回false表示验证失败
168+
"""
169+
token = self.token_of_request(url, body, content_type)
170+
authorization = 'QBox {0}'.format(token)
171+
return origin_authorization == authorization
172+
173+
@staticmethod
174+
def __copy_policy(policy, to, strict_policy):
175+
for k, v in policy.items():
176+
if k in _deprecated_policy_fields:
177+
raise ValueError(k + ' has deprecated')
178+
if (not strict_policy) or k in _policy_fields:
179+
to[k] = v
180+
181+
182+
class RequestsAuth(AuthBase):
183+
def __init__(self, auth):
184+
self.auth = auth
185+
186+
def __call__(self, r):
187+
token = None
188+
if r.body is not None and r.headers['Content-Type'] == 'application/x-www-form-urlencoded':
189+
token = self.auth.token_of_request(r.url, r.body, 'application/x-www-form-urlencoded')
190+
else:
191+
token = self.auth.token_of_request(r.url)
192+
r.headers['Authorization'] = 'QBox {0}'.format(token)
193+
return r

0 commit comments

Comments
 (0)