├── .gitignore ├── CreateFormData.ahk ├── JSON.ahk ├── LICENSE ├── README.md ├── exe └── smpic.exe ├── smpic.ahk └── smpic.ico /.gitignore: -------------------------------------------------------------------------------- 1 | /exe/log.txt 2 | /exe/smpic.ini 3 | /smpic.ini 4 | /.idea 5 | -------------------------------------------------------------------------------- /CreateFormData.ahk: -------------------------------------------------------------------------------- 1 | ;########################################################### 2 | ; Created by tmplinshi 3 | ; CreateFormData - Creates "multipart/form-data" for http post 4 | ; http://autohotkey.com/boards/viewtopic.php?f=6&t=7647 5 | ;########################################################### 6 | 7 | ; Used for WinHttp.WinHttpRequest.5.1, Msxml2.XMLHTTP ... 8 | CreateFormData(ByRef retData, ByRef retHeader, objParam) { 9 | New CreateFormData(retData, retHeader, objParam) 10 | } 11 | 12 | ; Used for WinInet 13 | Class CreateFormData { 14 | 15 | __New(ByRef retData, ByRef retHeader, objParam) { 16 | 17 | CRLF := "`r`n" 18 | 19 | ; Create a random Boundary 20 | Boundary := this.RandomBoundary() 21 | BoundaryLine := "------------------------------" . Boundary 22 | 23 | ; Loop input paramters 24 | binArrs := [] 25 | For k, v in objParam 26 | { 27 | If IsObject(v) { 28 | For i, FileName in v 29 | { 30 | str := BoundaryLine . CRLF 31 | . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF 32 | . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF 33 | binArrs.Push( BinArr_FromString(str) ) 34 | binArrs.Push( BinArr_FromFile(FileName) ) 35 | binArrs.Push( BinArr_FromString(CRLF) ) 36 | } 37 | } Else { 38 | str := BoundaryLine . CRLF 39 | . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF 40 | . v . CRLF 41 | binArrs.Push( BinArr_FromString(str) ) 42 | } 43 | } 44 | 45 | str := BoundaryLine . "--" . CRLF 46 | binArrs.Push( BinArr_FromString(str) ) 47 | 48 | ; Finish 49 | retData := BinArr_Join(binArrs*) 50 | retHeader := "multipart/form-data; boundary=----------------------------" . Boundary 51 | } 52 | 53 | RandomBoundary() { 54 | str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z" 55 | Sort, str, D| Random 56 | str := StrReplace(str, "|") 57 | Return SubStr(str, 1, 12) 58 | } 59 | 60 | MimeType(FileName) { 61 | n := FileOpen(FileName, "r").ReadUInt() 62 | Return (n = 0x474E5089) ? "image/png" 63 | : (n = 0x38464947) ? "image/gif" 64 | : (n&0xFFFF = 0x4D42 ) ? "image/bmp" 65 | : (n&0xFFFF = 0xD8FF ) ? "image/jpeg" 66 | : (n&0xFFFF = 0x4949 ) ? "image/tiff" 67 | : (n&0xFFFF = 0x4D4D ) ? "image/tiff" 68 | : "application/octet-stream" 69 | } 70 | 71 | } 72 | 73 | BinArr_FromString(str) { 74 | oADO := ComObjCreate("ADODB.Stream") 75 | 76 | oADO.Type := 2 ; adTypeText 77 | oADO.Mode := 3 ; adModeReadWrite 78 | oADO.Open 79 | oADO.Charset := "UTF-8" 80 | oADO.WriteText(str) 81 | 82 | oADO.Position := 0 83 | oADO.Type := 1 ; adTypeBinary 84 | oADO.Position := 3 ; Skip UTF-8 BOM 85 | return oADO.Read, oADO.Close 86 | } 87 | 88 | BinArr_FromFile(FileName) { 89 | oADO := ComObjCreate("ADODB.Stream") 90 | 91 | oADO.Type := 1 ; adTypeBinary 92 | oADO.Open 93 | oADO.LoadFromFile(FileName) 94 | return oADO.Read, oADO.Close 95 | } 96 | 97 | BinArr_Join(Arrays*) { 98 | oADO := ComObjCreate("ADODB.Stream") 99 | 100 | oADO.Type := 1 ; adTypeBinary 101 | oADO.Mode := 3 ; adModeReadWrite 102 | oADO.Open 103 | For i, arr in Arrays 104 | oADO.Write(arr) 105 | oADO.Position := 0 106 | return oADO.Read, oADO.Close 107 | } 108 | 109 | BinArr_ToString(BinArr, Encoding := "UTF-8") { 110 | oADO := ComObjCreate("ADODB.Stream") 111 | 112 | oADO.Type := 1 ; adTypeBinary 113 | oADO.Mode := 3 ; adModeReadWrite 114 | oADO.Open 115 | oADO.Write(BinArr) 116 | 117 | oADO.Position := 0 118 | oADO.Type := 2 ; adTypeText 119 | oADO.Charset := Encoding 120 | return oADO.ReadText, oADO.Close 121 | } 122 | 123 | BinArr_ToFile(BinArr, FileName) { 124 | oADO := ComObjCreate("ADODB.Stream") 125 | 126 | oADO.Type := 1 ; adTypeBinary 127 | oADO.Open 128 | oADO.Write(BinArr) 129 | oADO.SaveToFile(FileName, 2) 130 | oADO.Close 131 | } -------------------------------------------------------------------------------- /JSON.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * Lib: JSON.ahk 3 | * JSON lib for AutoHotkey. 4 | * Version: 5 | * v2.1.3 [updated 04/18/2016 (MM/DD/YYYY)] 6 | * License: 7 | * WTFPL [http://wtfpl.net/] 8 | * Requirements: 9 | * Latest version of AutoHotkey (v1.1+ or v2.0-a+) 10 | * Installation: 11 | * Use #Include JSON.ahk or copy into a function library folder and then 12 | * use #Include 13 | * Links: 14 | * GitHub: - https://github.com/cocobelgica/AutoHotkey-JSON 15 | * Forum Topic - http://goo.gl/r0zI8t 16 | * Email: - cocobelgica gmail com 17 | */ 18 | 19 | 20 | /** 21 | * Class: JSON 22 | * The JSON object contains methods for parsing JSON and converting values 23 | * to JSON. Callable - NO; Instantiable - YES; Subclassable - YES; 24 | * Nestable(via #Include) - NO. 25 | * Methods: 26 | * Load() - see relevant documentation before method definition header 27 | * Dump() - see relevant documentation before method definition header 28 | */ 29 | class JSON 30 | { 31 | /** 32 | * Method: Load 33 | * Parses a JSON string into an AHK value 34 | * Syntax: 35 | * value := JSON.Load( text [, reviver ] ) 36 | * Parameter(s): 37 | * value [retval] - parsed value 38 | * text [in, ByRef] - JSON formatted string 39 | * reviver [in, opt] - function object, similar to JavaScript's 40 | * JSON.parse() 'reviver' parameter 41 | */ 42 | class Load extends JSON.Functor 43 | { 44 | Call(self, ByRef text, reviver:="") 45 | { 46 | this.rev := IsObject(reviver) ? reviver : false 47 | ; Object keys(and array indices) are temporarily stored in arrays so that 48 | ; we can enumerate them in the order they appear in the document/text instead 49 | ; of alphabetically. Skip if no reviver function is specified. 50 | this.keys := this.rev ? {} : false 51 | 52 | static quot := Chr(34), bashq := "\" . quot 53 | , json_value := quot . "{[01234567890-tfn" 54 | , json_value_or_array_closing := quot . "{[]01234567890-tfn" 55 | , object_key_or_object_closing := quot . "}" 56 | 57 | key := "" 58 | is_key := false 59 | root := {} 60 | stack := [root] 61 | next := json_value 62 | pos := 0 63 | 64 | while ((ch := SubStr(text, ++pos, 1)) != "") { 65 | if InStr(" `t`r`n", ch) 66 | continue 67 | if !InStr(next, ch, 1) 68 | this.ParseError(next, text, pos) 69 | 70 | holder := stack[1] 71 | is_array := holder.IsArray 72 | 73 | if InStr(",:", ch) { 74 | next := (is_key := !is_array && ch == ",") ? quot : json_value 75 | 76 | } else if InStr("}]", ch) { 77 | ObjRemoveAt(stack, 1) 78 | next := stack[1]==root ? "" : stack[1].IsArray ? ",]" : ",}" 79 | 80 | } else { 81 | if InStr("{[", ch) { 82 | ; Check if Array() is overridden and if its return value has 83 | ; the 'IsArray' property. If so, Array() will be called normally, 84 | ; otherwise, use a custom base object for arrays 85 | static json_array := Func("Array").IsBuiltIn || ![].IsArray ? {IsArray: true} : 0 86 | 87 | ; sacrifice readability for minor(actually negligible) performance gain 88 | (ch == "{") 89 | ? ( is_key := true 90 | , value := {} 91 | , next := object_key_or_object_closing ) 92 | ; ch == "[" 93 | : ( value := json_array ? new json_array : [] 94 | , next := json_value_or_array_closing ) 95 | 96 | ObjInsertAt(stack, 1, value) 97 | 98 | if (this.keys) 99 | this.keys[value] := [] 100 | 101 | } else { 102 | if (ch == quot) { 103 | i := pos 104 | while (i := InStr(text, quot,, i+1)) { 105 | value := StrReplace(SubStr(text, pos+1, i-pos-1), "\\", "\u005c") 106 | 107 | static tail := A_AhkVersion<"2" ? 0 : -1 108 | if (SubStr(value, tail) != "\") 109 | break 110 | } 111 | 112 | if (!i) 113 | this.ParseError("'", text, pos) 114 | 115 | value := StrReplace(value, "\/", "/") 116 | , value := StrReplace(value, bashq, quot) 117 | , value := StrReplace(value, "\b", "`b") 118 | , value := StrReplace(value, "\f", "`f") 119 | , value := StrReplace(value, "\n", "`n") 120 | , value := StrReplace(value, "\r", "`r") 121 | , value := StrReplace(value, "\t", "`t") 122 | 123 | pos := i ; update pos 124 | 125 | i := 0 126 | while (i := InStr(value, "\",, i+1)) { 127 | if !(SubStr(value, i+1, 1) == "u") 128 | this.ParseError("\", text, pos - StrLen(SubStr(value, i+1))) 129 | 130 | uffff := Abs("0x" . SubStr(value, i+2, 4)) 131 | if (A_IsUnicode || uffff < 0x100) 132 | value := SubStr(value, 1, i-1) . Chr(uffff) . SubStr(value, i+6) 133 | } 134 | 135 | if (is_key) { 136 | key := value, next := ":" 137 | continue 138 | } 139 | 140 | } else { 141 | value := SubStr(text, pos, i := RegExMatch(text, "[\]\},\s]|$",, pos)-pos) 142 | 143 | static number := "number", integer :="integer" 144 | if value is %number% 145 | { 146 | if value is %integer% 147 | value += 0 148 | } 149 | else if (value == "true" || value == "false") 150 | value := %value% + 0 151 | else if (value == "null") 152 | value := "" 153 | else 154 | ; we can do more here to pinpoint the actual culprit 155 | ; but that's just too much extra work. 156 | this.ParseError(next, text, pos, i) 157 | 158 | pos += i-1 159 | } 160 | 161 | next := holder==root ? "" : is_array ? ",]" : ",}" 162 | } ; If InStr("{[", ch) { ... } else 163 | 164 | is_array? key := ObjPush(holder, value) : holder[key] := value 165 | 166 | if (this.keys && this.keys.HasKey(holder)) 167 | this.keys[holder].Push(key) 168 | } 169 | 170 | } ; while ( ... ) 171 | 172 | return this.rev ? this.Walk(root, "") : root[""] 173 | } 174 | 175 | ParseError(expect, ByRef text, pos, len:=1) 176 | { 177 | static quot := Chr(34), qurly := quot . "}" 178 | 179 | line := StrSplit(SubStr(text, 1, pos), "`n", "`r").Length() 180 | col := pos - InStr(text, "`n",, -(StrLen(text)-pos+1)) 181 | msg := Format("{1}`n`nLine:`t{2}`nCol:`t{3}`nChar:`t{4}" 182 | , (expect == "") ? "Extra data" 183 | : (expect == "'") ? "Unterminated string starting at" 184 | : (expect == "\") ? "Invalid \escape" 185 | : (expect == ":") ? "Expecting ':' delimiter" 186 | : (expect == quot) ? "Expecting object key enclosed in double quotes" 187 | : (expect == qurly) ? "Expecting object key enclosed in double quotes or object closing '}'" 188 | : (expect == ",}") ? "Expecting ',' delimiter or object closing '}'" 189 | : (expect == ",]") ? "Expecting ',' delimiter or array closing ']'" 190 | : InStr(expect, "]") ? "Expecting JSON value or array closing ']'" 191 | : "Expecting JSON value(string, number, true, false, null, object or array)" 192 | , line, col, pos) 193 | 194 | static offset := A_AhkVersion<"2" ? -3 : -4 195 | throw Exception(msg, offset, SubStr(text, pos, len)) 196 | } 197 | 198 | Walk(holder, key) 199 | { 200 | value := holder[key] 201 | if IsObject(value) { 202 | for i, k in this.keys[value] { 203 | ; check if ObjHasKey(value, k) ?? 204 | v := this.Walk(value, k) 205 | if (v != JSON.Undefined) 206 | value[k] := v 207 | else 208 | ObjDelete(value, k) 209 | } 210 | } 211 | 212 | return this.rev.Call(holder, key, value) 213 | } 214 | } 215 | 216 | /** 217 | * Method: Dump 218 | * Converts an AHK value into a JSON string 219 | * Syntax: 220 | * str := JSON.Dump( value [, replacer, space ] ) 221 | * Parameter(s): 222 | * str [retval] - JSON representation of an AHK value 223 | * value [in] - any value(object, string, number) 224 | * replacer [in, opt] - function object, similar to JavaScript's 225 | * JSON.stringify() 'replacer' parameter 226 | * space [in, opt] - similar to JavaScript's JSON.stringify() 227 | * 'space' parameter 228 | */ 229 | class Dump extends JSON.Functor 230 | { 231 | Call(self, value, replacer:="", space:="") 232 | { 233 | this.rep := IsObject(replacer) ? replacer : "" 234 | 235 | this.gap := "" 236 | if (space) { 237 | static integer := "integer" 238 | if space is %integer% 239 | Loop, % ((n := Abs(space))>10 ? 10 : n) 240 | this.gap .= " " 241 | else 242 | this.gap := SubStr(space, 1, 10) 243 | 244 | this.indent := "`n" 245 | } 246 | 247 | return this.Str({"": value}, "") 248 | } 249 | 250 | Str(holder, key) 251 | { 252 | value := holder[key] 253 | 254 | if (this.rep) 255 | value := this.rep.Call(holder, key, ObjHasKey(holder, key) ? value : JSON.Undefined) 256 | 257 | if IsObject(value) { 258 | ; Check object type, skip serialization for other object types such as 259 | ; ComObject, Func, BoundFunc, FileObject, RegExMatchObject, Property, etc. 260 | static type := A_AhkVersion<"2" ? "" : Func("Type") 261 | if (type ? type.Call(value) == "Object" : ObjGetCapacity(value) != "") { 262 | if (this.gap) { 263 | stepback := this.indent 264 | this.indent .= this.gap 265 | } 266 | 267 | is_array := value.IsArray 268 | ; Array() is not overridden, rollback to old method of 269 | ; identifying array-like objects. Due to the use of a for-loop 270 | ; sparse arrays such as '[1,,3]' are detected as objects({}). 271 | if (!is_array) { 272 | for i in value 273 | is_array := i == A_Index 274 | until !is_array 275 | } 276 | 277 | str := "" 278 | if (is_array) { 279 | Loop, % value.Length() { 280 | if (this.gap) 281 | str .= this.indent 282 | 283 | v := this.Str(value, A_Index) 284 | str .= (v != "") ? v . "," : "null," 285 | } 286 | } else { 287 | colon := this.gap ? ": " : ":" 288 | for k in value { 289 | v := this.Str(value, k) 290 | if (v != "") { 291 | if (this.gap) 292 | str .= this.indent 293 | 294 | str .= this.Quote(k) . colon . v . "," 295 | } 296 | } 297 | } 298 | 299 | if (str != "") { 300 | str := RTrim(str, ",") 301 | if (this.gap) 302 | str .= stepback 303 | } 304 | 305 | if (this.gap) 306 | this.indent := stepback 307 | 308 | return is_array ? "[" . str . "]" : "{" . str . "}" 309 | } 310 | 311 | } else ; is_number ? value : "value" 312 | return ObjGetCapacity([value], 1)=="" ? value : this.Quote(value) 313 | } 314 | 315 | Quote(string) 316 | { 317 | static quot := Chr(34), bashq := "\" . quot 318 | 319 | if (string != "") { 320 | string := StrReplace(string, "\", "\\") 321 | ; , string := StrReplace(string, "/", "\/") ; optional in ECMAScript 322 | , string := StrReplace(string, quot, bashq) 323 | , string := StrReplace(string, "`b", "\b") 324 | , string := StrReplace(string, "`f", "\f") 325 | , string := StrReplace(string, "`n", "\n") 326 | , string := StrReplace(string, "`r", "\r") 327 | , string := StrReplace(string, "`t", "\t") 328 | 329 | static rx_escapable := A_AhkVersion<"2" ? "O)[^\x20-\x7e]" : "[^\x20-\x7e]" 330 | while RegExMatch(string, rx_escapable, m) 331 | string := StrReplace(string, m.Value, Format("\u{1:04x}", Ord(m.Value))) 332 | } 333 | 334 | return quot . string . quot 335 | } 336 | } 337 | 338 | /** 339 | * Property: Undefined 340 | * Proxy for 'undefined' type 341 | * Syntax: 342 | * undefined := JSON.Undefined 343 | * Remarks: 344 | * For use with reviver and replacer functions since AutoHotkey does not 345 | * have an 'undefined' type. Returning blank("") or 0 won't work since these 346 | * can't be distnguished from actual JSON values. This leaves us with objects. 347 | * Replacer() - the caller may return a non-serializable AHK objects such as 348 | * ComObject, Func, BoundFunc, FileObject, RegExMatchObject, and Property to 349 | * mimic the behavior of returning 'undefined' in JavaScript but for the sake 350 | * of code readability and convenience, it's better to do 'return JSON.Undefined'. 351 | * Internally, the property returns a ComObject with the variant type of VT_EMPTY. 352 | */ 353 | Undefined[] 354 | { 355 | get { 356 | static empty := {}, vt_empty := ComObject(0, &empty, 1) 357 | return vt_empty 358 | } 359 | } 360 | 361 | class Functor 362 | { 363 | __Call(method, ByRef arg, args*) 364 | { 365 | ; When casting to Call(), use a new instance of the "function object" 366 | ; so as to avoid directly storing the properties(used across sub-methods) 367 | ; into the "function object" itself. 368 | if IsObject(method) 369 | return (new this).Call(method, arg, args*) 370 | else if (method == "") 371 | return (new this).Call(arg, args*) 372 | } 373 | } 374 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ob 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 | # smpic 2 | Windows下面的SM.MS图床上传工具 3 | 4 | --- 5 | 看到有网友说mac下面有好多图床上传小工具,Windows下面却没有。然后就花了个把小时用AHK撸了这个小工具。 6 | 7 | ### 使用方式 8 | 1. 选中图片(可多选) 9 | 2. 按快捷键(可配置),默认 Ctrl+Alt+S 10 | 3. 图片地址已经保存到剪切板了(可配置支持markdown) 11 | 12 | 因为SM.MS最新API更新了上传方式,只允许登录用户上传图片,所以需要使用该工具的朋友需要 [登录后台](https://sm.ms/home/apitoken) 生成自己的Secret Token,在配置文件里面替换为自己的。 13 | **具体配置可参考smpic.ini里面的说明** 14 | 15 | ### 下载地址 16 | [smpic.exe](https://github.com/kookob/smpic/blob/master/exe/smpic.exe?raw=true) 17 | 18 | ### 致谢 19 | 感谢 [@Showfom](https://github.com/Showfom) 提供的图床(https://sm.ms) 20 | 感谢网上提供的AHK类库(CreateFormData.ahk和JSON.ahk) 21 | -------------------------------------------------------------------------------- /exe/smpic.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kookob/smpic/d0d59706409b6c9966213b54e30d1defe704aa4b/exe/smpic.exe -------------------------------------------------------------------------------- /smpic.ahk: -------------------------------------------------------------------------------- 1 | ;########################################################### 2 | ; @author ob 3 | ; @version 2.0.1 4 | ; @date 20220305 5 | ; http://github.com/kookob/smpic 6 | ;########################################################### 7 | #SingleInstance,Force 8 | #NoEnv 9 | SendMode,Input 10 | DetectHiddenWindows,On 11 | SetWinDelay,0 12 | SetKeyDelay,0 13 | SetControlDelay,0 14 | SetBatchLines,10ms 15 | CoordMode,Mouse,Screen 16 | 17 | applicationname=smpic 18 | Gosub,READINI 19 | Gosub,TRAYMENU 20 | 21 | #Include %A_ScriptDir%\CreateFormData.ahk 22 | #Include %A_ScriptDir%\JSON.ahk 23 | 24 | ;上传图片 25 | upload(file, secretToken){ 26 | IfExist % file 27 | { 28 | objParam := {"format":"json", "smfile": [file]} 29 | CreateFormData(PostData, hdr_ContentType, objParam) 30 | whr := ComObjCreate("WinHttp.WinHttpRequest.5.1") 31 | whr.Open("POST", "https://sm.ms/api/v2/upload", True) 32 | whr.SetRequestHeader("Content-Type", hdr_ContentType) 33 | whr.SetRequestHeader("Authorization", secretToken) 34 | whr.Send(PostData) 35 | whr.WaitForResponse() 36 | return whr.ResponseText 37 | } 38 | } 39 | 40 | IniRead, key, %applicationname%.ini, Settings, key, ^!s 41 | IniRead, urlType, %applicationname%.ini, Settings, urlType, 0 42 | IniRead, secretToken, %applicationname%.ini, Settings, secretToken, ReplaceWithYourSecretToken 43 | Hotkey, %key%, UploadLabel, On 44 | return 45 | 46 | UploadLabel: 47 | clipboard = 48 | send,^c 49 | ClipWait 50 | filepathList = %clipboard% 51 | clipboard = 52 | Loop, parse, filepathList, `n, `r 53 | { 54 | filepath = %A_LoopField% 55 | SplitPath, filepath, filename 56 | result := upload(filepath, secretToken) 57 | if(result <> ""){ 58 | resultJson := JSON.Load(result) 59 | if(resultJson.code = "success"){ 60 | if(clipboard <> "") { 61 | clipboard := clipboard . "`n" 62 | } 63 | if(urlType = 1) { 64 | clipboard := clipboard . "![" . filename . "](" . resultJson.data.url . ")" ;markdown地址 65 | } else { 66 | clipboard := clipboard . resultJson.data.url ;原始地址 67 | } 68 | ToolTip, (%A_Index%)上传成功,url已复制到剪切板, A_CaretX, A_CaretY+20 69 | Sleep 1000 70 | ToolTip, 71 | } else if(resultJson.code = "image_repeated"){ 72 | msg := "(" . A_Index . ")" . resultJson.error . "`n上传图片已存在,原有url已复制到剪切板" 73 | msgbox % msg 74 | if(clipboard <> "") { 75 | clipboard := clipboard . "`n" 76 | } 77 | clipboard := clipboard . resultJson.images ;原始地址 78 | } else { 79 | msgbox % resultJson.error 80 | } 81 | } else { 82 | msgbox 上传失败,请稍候再试! 83 | } 84 | } 85 | return 86 | 87 | TRAYMENU: 88 | Menu,Tray,NoStandard 89 | Menu,Tray,DeleteAll 90 | Menu,Tray,Add,启用(&E),TOGGLE 91 | Menu,Tray,Add, 92 | Menu,Tray,Add,设置(&S),SETTINGS 93 | Menu,Tray,Add,重启(&R),RESTART 94 | Menu,Tray,Add, 95 | Menu,Tray,Add,关于(&A),ABOUT 96 | Menu,Tray,Add,退出(&Q),EXIT 97 | Menu,Tray,ToggleCheck,启用(&E) 98 | Menu,Tray,Tip,%applicationname% 99 | Return 100 | 101 | TOGGLE: 102 | Menu,Tray,ToggleCheck,启用(&E) 103 | Pause,Toggle 104 | Return 105 | 106 | SETTINGS: 107 | Gosub,READINI 108 | Run,%applicationname%.ini 109 | Return 110 | 111 | RESTART: 112 | Reload 113 | Return 114 | 115 | EXIT: 116 | ExitApp 117 | 118 | READINI: 119 | IfNotExist,%applicationname%.ini 120 | { 121 | ini=;%applicationname%.ini 122 | ini=%ini%`n`;key: 快捷键设置:对应按键win(#),ctrl(^),alt(!),shitf(+),默认是(^!s) 123 | ini=%ini%`n`;urlType: 返回结果url:0-原始地址(默认),1-markdown地址 124 | ini=%ini%`n`;secretToken: sm.ms账号登录获取: https://sm.ms/home/apitoken 125 | ini=%ini%`n`;(改完配置,重启生效) 126 | ini=%ini%`n 127 | ini=%ini%`n[Settings] 128 | ini=%ini%`nkey=^!s 129 | ini=%ini%`nurlType=0 130 | ini=%ini%`nsecretToken=请配置自己的Secret Token 131 | ini=%ini%`n 132 | FileAppend,%ini%,%applicationname%.ini 133 | ini= 134 | } 135 | Return 136 | 137 | ABOUT: 138 | Gui,99:Destroy 139 | Gui,99:Margin,15,15 140 | 141 | Gui,99:Font,Bold 142 | Gui,99:Add,Text,y+10, %applicationname% v2.0.1 (20220305) 143 | Gui,99:Font 144 | Gui,99:Add,Text,y+10,选中图片(可多选)按快捷键(默认:Ctrl+Alt+S)上传图片到sm.ms,保存图片地址到剪切板 145 | Gui,99:Font,CBlue Underline 146 | Gui,99:Font 147 | 148 | Gui,99:Font,Bold 149 | Gui,99:Add,Text,y+20,github地址(★) 150 | Gui,99:Font 151 | Gui,99:Font,CBlue Underline 152 | Gui,99:Add,Link,y+5, http://github.com/kookob/smpic 153 | 154 | Gui,99:Font,Bold 155 | Gui,99:Add,Text,y+20,致谢 156 | Gui,99:Font 157 | Gui,99:Add,Text,y+10,感谢 https://sm.ms 提供的图床 158 | 159 | Gui,99:Show,,%applicationname% About 160 | hCurs:=DllCall("LoadCursor","UInt",NULL,"Int",32649,"UInt") 161 | OnMessage(0x200,"WM_MOUSEMOVE") 162 | Return 163 | -------------------------------------------------------------------------------- /smpic.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kookob/smpic/d0d59706409b6c9966213b54e30d1defe704aa4b/smpic.ico --------------------------------------------------------------------------------