通过Web Short Message在安卓手机群发短信

许多实用脚本和封装函数, 可以让您编写脚本更加便捷高效
User avatar
amnesiac
Posts: 186
Joined: 22 Nov 2013, 03:08
Location: Egret Island, China
Contact:

通过Web Short Message在安卓手机群发短信

03 Jul 2016, 21:50

注:本脚本修改自Autohotkey+Web Short Message安卓短信接口,解决了在Win10 64位上U64执行时中文短信乱码的问题,同时自动切割超出140个半角字符长度的短信以错误,增加了图形界面,在这个过程中得到thinkai的指导。下一步计划把接收短信加到脚本中。
使用说明
  1. 在安卓手机上安装Web Short Message,并设置发送短信权限为允许;
  2. 将手机连接到WIFI,记下当前分配的IP地址;
  3. 在手机上打开Web Short Message,记下IP地址后的两个数字;
  4. 将电脑连接到与手机相同的局域网;
  5. 在电脑上打开本脚本,并填入前面记录的IP地址及相应的两个数字,如:
注意事项
  • 手机上是否启动了Web Short Message;
  • 手机屏幕是否保持常量(尽量保持Web Short Message在前台运行,后台运行时常会被限制);
  • 当前电脑与手机是否在同一局域网;
  • 手机中Web Short Message是否具备发短信权限;
  • 群发大量短信时,建议设置较大的间隔,以避开手机的限制(在小米手机上遇到过连续发送10多条,在权限设为允许后都会提示是否允许,默认拒绝)。
群发短信工具.zip
(532.65 KiB) Downloaded 134 times

Code: Select all

;Autohotkey+Web Short Message(com.wangtai.smstwoman)短信接口
#SingleInstance, Force


AppName := "群发短信"
AppVer := "0.8.0812"
Author := "阿平@香海禅寺"
AppTitle := AppName AppVer " by " Author
Notice =
(
1. 手机上是否启动了Web Short Message;`n
2. 手机屏幕是否保持常量;`n
3. 当前电脑与手机是否在同一局域网;`n
4. 手机中Web Short Message是否具备发短信权限。`n
请在浏览器中打开管理网址,测试能否正常发送短信。
)

gosub, CreateGUI

return

CreateGUI:
Gui, Font, S12 CDefault, Verdana
Gui, Add, Tab2, x12 y9 w450 h320 , 基本信息|号码列表|注意事项
Gui, Tab, 基本信息
Gui, Add, Text, x32 y49 w100 h60 section, 目标地址
Gui, Add, Text, xs ys+32 , 发送间隔
Gui, Add, Text, x232 ys+32, (单位:秒,建议值10)
Gui, Add, Edit, x112 y49 w300 Section vMsgAddress, 
Gui, Add, Edit, xs yp+32 w100 vMsgPeriod, 10

Gui, Add, Text, x32 y113 w400 h30 , 下面是即将发送的短信内容,请调整确认。
Gui, Add, Edit, x32 y143 w410 h160 vMessageContent, 
Gui, Tab, 号码列表
Gui, Add, Text, x32 y49 w400 h30 , 请把发送号码从Excel复制粘贴到此处,每行一个。
Gui, Add, Edit, x32 y79 w410 h240 vNumberList, 
Gui, Tab, 注意事项
Gui, Add, Text, x32 y49 w400 h280 , %Notice%
Gui, Tab
Gui, Add, StatusBar, x32 y339 w300, 准备状态。
Gui, Add, Button, x362 y339 w100 h30 gSend, 发送

; Generated using SmartGUI Creator for SciTE
Gui, Show, w479 h379, %AppTitle%
return

Send:
Gui, Submit, NoHide
gosub, MassMessage
return

GuiClose:
ExitApp
return

MassMessage:
; 判断网址是否有效,即HTTP状态码是否200
if (RegExReplace(HttpQueryInfo(MsgAddress, 19),"`n") <> 200)
{
	MsgBox, 4096, %AppTitle%, 刚才输入的网址无法访问,请在浏览器中测试网址后再启动本应用。
	return
}
;获取参数
RegExMatch(MsgAddress,"^(http://.*:\d*/)(\d{4})",parameter)
global parameter1
global parameter2

; 获取号码总数
RegExReplace(NumberList, "\d{9}", "", TotalAmount)
; 发送短信
ErrorNumberList := ""	; 发送失败列表,每行一个
SuccessfulAmount := 0
Loop, Parse, NumberList, `n, `r%A_Space%%A_Tab%
{
	if(StrLen(A_LoopField)=11)
	{
		SB_SetText("发送状态:共" TotalAmount "个,正在发送第" A_Index "个:" A_LoopField)
		if(StrLen(MessageContent)<70)
			ThisSendState := SendMsg(A_LoopField, MessageContent)
		else
		{
			Sections := Ceil(StrLen(MessageContent)/65)
			Loop, % Sections
			{
				ThisSection := SubStr(MessageContent, (A_Index - 1) * 65 + 1, 65)
				ThisSection .= "(" A_Index "/" Sections ")"
				ThisSendState := SendMsg(A_LoopField, ThisSection)
			}
		}
		if ThisSendState = 1
			SuccessfulAmount += 1
		else
			ErrorNumberList .= A_LoopField "`n"
		SB_SetText("休息中……")
		Sleep, % MsgPeriod * 1000
	}
	
}

Clipboard := ErrorNumberList
StatusText := "发送完成:共" TotalAmount "个,成功发送" SuccessfulAmount "个" . (ErrorNumberList? ",发送失败的号码在剪贴板中。":"。")
SB_SetText(StatusText)
return

CountWord(str)
{
	n := 0
	Loop, parse, str
	{
		if(Asc(A_LoopField)<256)
			n := n+1
		else
			n := n+2
	}
}

;接收回复
NewMessage := GetNewMsg()
From :=  NewMessage[1]["person"] ? NewMessage[1]["person"] "(" NewMessage[1]["address"] ")" : "未知(" NewMessage[1]["address"] ")"
MsgBox % "From:" From "`n" NewMessage[1]["body"]

;获取联系人
Contact := GetContacts()
for k,v in Contact
{
	MsgBox, 64, 提示, 第一个联系人是%v%`,号码是%k%
	Return
}
return

;发短信函数
SendMsg(to,msg){ ;号码,消息
	;~ msg := urlencode(msg, "cp0")
	Return URLDownloadToVar(parameter1 "send/" parameter2 "/" to "/", "utf-8","POST","msg=" msg)
}

;获取新消息函数(调用后收到的)
GetNewMsg(){
;返回数组说明
;数组[1]:
;	_id:消息ID
;	address:号码
;	body:短信正文
;	date:收信Uinx时间戳
;	person:联系人
	Loop
	{
		res := URLDownloadToVar(parameter1 "get_new_msg/" parameter2 "/", "utf-8","POST") ;获取最新短信的JSON数据
		if (strlen(res)>2)
			Return json_toobj(res)
		Sleep, 100
	}
}

;获取联系人函数
GetContacts(){
obj := {} ;初始数组
res := URLDownloadToVar(parameter1 "get_contacts/" parameter2 "/", "utf-8","POST") ;获取联系人的JSON数据
;处理格式问题
StringTrimLeft, res, res, 1
StringTrimRight, res, res, 1
StringSplit, var, res, `,
loop % var0
{
	tmp_var := var%A_index%
	RegExMatch(tmp_var,"""(.*)"":""(.*)""",m)
	m1 := RegExReplace(m1,"\s","")
	m1 := RegExReplace(m1,"\+86","")
	obj["" m1] := m2 ;注意此处数组key的类型
}
Return obj ;返回数组  obj[电话号码] := 联系人姓名
}

URLDownloadToVar(url, Encoding = "",Method="GET",postData=""){ ;网址,编码,请求方式,post数据
	hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
	if Method = GET
	{
		hObject.Open("GET",url)
		Try
			hObject.Send()
		catch e
			return -1
	}
	else if Method = POST
	{
		hObject.Open("POST",url,false)
		hObject.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
		Try
			hObject.Send(postData)
		catch e
			return -1
	}

	if Encoding
	{
		oADO := ComObjCreate("adodb.stream")
		oADO.Type := 1
		oADO.Mode := 3
		oADO.Open()
		oADO.Write(hObject.ResponseBody)
		oADO.Position := 0
		oADO.Type := 2
		oADO.Charset := Encoding
		return oADO.ReadText(), oADO.Close()
	}
	return hObject.ResponseText
}

/* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; HttpQueryInfo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
https://autohotkey.com/board/topic/9569-dllcall-httpqueryinfo-get-http-headers/
https://msdn.microsoft.com/en-us/library/windows/desktop/aa385351
https://msdn.microsoft.com/en-us/library/windows/desktop/aa384325
QueryInfoFlag:

HTTP_QUERY_RAW_HEADERS = 21
Receives all the headers returned by the server.
Each header is terminated by "\0". An additional "\0" terminates the list of headers.

HTTP_QUERY_CONTENT_LENGTH = 5
Retrieves the size of the resource, in bytes.

HTTP_QUERY_CONTENT_TYPE = 1
Receives the content type of the resource (such as text/html).

HTTP_QUERY_STATUS_CODE
19
Receives the status code returned by the server.

Find more at: http://msdn.microsoft.com/library/en-us/wininet/wininet/query_info_flags.asp

Proxy Settings:

INTERNET_OPEN_TYPE_PRECONFIG                    0   // use registry configuration
INTERNET_OPEN_TYPE_DIRECT                       1   // direct to net
INTERNET_OPEN_TYPE_PROXY                        3   // via named proxy
INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY  4   // prevent using java/script/INS

*/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 

HttpQueryInfo(URL, QueryInfoFlag=21, Proxy="", ProxyBypass="") {
hModule := DllCall("LoadLibrary", "str", "wininet.dll") 

If (Proxy != "")
AccessType=3
Else
AccessType=1

io_hInternet := DllCall("wininet\InternetOpen"
, "str", "" ;lpszAgent
, "uint", AccessType
, "str", Proxy
, "str", ProxyBypass
, "uint", 0) ;dwFlags
If (ErrorLevel != 0 or io_hInternet = 0) {
DllCall("FreeLibrary", "uint", hModule)
return, -1
}

iou_hInternet := DllCall("wininet\InternetOpenUrl"
, "uint", io_hInternet
, "str", url
, "str", "" ;lpszHeaders
, "uint", 0 ;dwHeadersLength
, "uint", 0x80000000 ;dwFlags: INTERNET_FLAG_RELOAD = 0x80000000 // retrieve the original item
, "uint", 0) ;dwContext
If (ErrorLevel != 0 or iou_hInternet = 0) {
DllCall("FreeLibrary", "uint", hModule)
return, -1
}

VarSetCapacity(buffer, 1024, 0)
VarSetCapacity(buffer_len, 4, 0)

Loop, 5
{
  hqi := DllCall("wininet\HttpQueryInfo"
  , "uint", iou_hInternet
  , "uint", QueryInfoFlag ;dwInfoLevel
  , "uint", &buffer
  , "uint", &buffer_len
  , "uint", 0) ;lpdwIndex
  If (hqi = 1) {
    hqi=success
    break
  }
}

IfNotEqual, hqi, success, SetEnv, res, timeout

If (hqi = "success") {
p := &buffer
Loop
{
  l := DllCall("lstrlen", "UInt", p)
  VarSetCapacity(tmp_var, l+1, 0)
  DllCall("lstrcpy", "Str", tmp_var, "UInt", p)
  p += l + 1  
  res := res  . "`n" . tmp_var
  If (*p = 0)
  Break
}
StringTrimLeft, res, res, 1
}

DllCall("wininet\InternetCloseHandle",  "uint", iou_hInternet)
DllCall("wininet\InternetCloseHandle",  "uint", io_hInternet)
DllCall("FreeLibrary", "uint", hModule)

return, res
}

json_toobj(str){

	quot := """" ; firmcoded specifically for readability. Hardcode for (minor) performance gain
	ws := "`t`n`r " Chr(160) ; whitespace plus NBSP. This gets trimmed from the markup
	obj := {} ; dummy object
	objs := [] ; stack
	keys := [] ; stack
	isarrays := [] ; stack
	literals := [] ; queue
	y := nest := 0

; First pass swaps out literal strings so we can parse the markup easily
	StringGetPos, z, str, %quot% ; initial seek
	while !ErrorLevel
	{
		; Look for the non-literal quote that ends this string. Encode literal backslashes as '\u005C' because the
		; '\u..' entities are decoded last and that prevents literal backslashes from borking normal characters
		StringGetPos, x, str, %quot%,, % z + 1
		while !ErrorLevel
		{
			StringMid, key, str, z + 2, x - z - 1
			StringReplace, key, key, \\, \u005C, A
			If SubStr( key, 0 ) != "\"
				Break
			StringGetPos, x, str, %quot%,, % x + 1
		}
	;	StringReplace, str, str, %quot%%t%%quot%, %quot% ; this might corrupt the string
		str := ( z ? SubStr( str, 1, z ) : "" ) quot SubStr( str, x + 2 ) ; this won't

	; Decode entities
		StringReplace, key, key, \%quot%, %quot%, A
		StringReplace, key, key, \b, % Chr(08), A
		StringReplace, key, key, \t, % A_Tab, A
		StringReplace, key, key, \n, `n, A
		StringReplace, key, key, \f, % Chr(12), A
		StringReplace, key, key, \r, `r, A
		StringReplace, key, key, \/, /, A
		while y := InStr( key, "\u", 0, y + 1 )
			if ( A_IsUnicode || Abs( "0x" SubStr( key, y + 2, 4 ) ) < 0x100 )
				key := ( y = 1 ? "" : SubStr( key, 1, y - 1 ) ) Chr( "0x" SubStr( key, y + 2, 4 ) ) SubStr( key, y + 6 )

		literals.insert(key)

		StringGetPos, z, str, %quot%,, % z + 1 ; seek
	}

; Second pass parses the markup and builds the object iteratively, swapping placeholders as they are encountered
	key := isarray := 1

	; The outer loop splits the blob into paths at markers where nest level decreases
	Loop Parse, str, % "]}"
	{
		StringReplace, str, A_LoopField, [, [], A ; mark any array open-brackets

		; This inner loop splits the path into segments at markers that signal nest level increases
		Loop Parse, str, % "[{"
		{
			; The first segment might contain members that belong to the previous object
			; Otherwise, push the previous object and key to their stacks and start a new object
			if ( A_Index != 1 )
			{
				objs.insert( obj )
				isarrays.insert( isarray )
				keys.insert( key )
				obj := {}
				isarray := key := Asc( A_LoopField ) = 93
			}

			; arrrrays are made by pirates and they have index keys
			if ( isarray )
			{
				Loop Parse, A_LoopField, `,, % ws "]"
					if ( A_LoopField != "" )
						obj[key++] := A_LoopField = quot ? literals.remove(1) : A_LoopField
			}
			; otherwise, parse the segment as key/value pairs
			else
			{
				Loop Parse, A_LoopField, `,
					Loop Parse, A_LoopField, :, % ws
						if ( A_Index = 1 )
							key := A_LoopField = quot ? literals.remove(1) : A_LoopField
						else if ( A_Index = 2 && A_LoopField != "" )
							obj[key] := A_LoopField = quot ? literals.remove(1) : A_LoopField
			}
			nest += A_Index > 1
		} ; Loop Parse, str, % "[{"

		If !--nest
			Break

		; Insert the newly closed object into the one on top of the stack, then pop the stack
		pbj := obj
		obj := objs.remove()
		obj[key := keys.remove()] := pbj
		If ( isarray := isarrays.remove() )
			key++

	} ; Loop Parse, str, % "]}"

	Return obj
}

Return to “脚本函数”

Who is online

Users browsing this forum: No registered users and 3 guests