student.go 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276
  1. package student
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "math/rand"
  11. "mime/multipart"
  12. "net/http"
  13. "os"
  14. "ouchn/config"
  15. "regexp"
  16. "runtime"
  17. "strings"
  18. "sync"
  19. "time"
  20. "github.com/syndtr/goleveldb/leveldb"
  21. "github.com/tebeka/selenium"
  22. "github.com/tebeka/selenium/chrome"
  23. "github.com/tidwall/gjson"
  24. )
  25. func GetRecord(filename string) map[string]bool {
  26. ret := map[string]bool{}
  27. fi, err := os.Open(filename)
  28. if err != nil {
  29. return ret
  30. }
  31. defer fi.Close()
  32. br := bufio.NewReader(fi)
  33. for {
  34. a, _, c := br.ReadLine()
  35. //fmt.Printf("xx:%s\n", a)
  36. if c == io.EOF {
  37. break
  38. }
  39. array := strings.Split(string(a), " ")
  40. if len(array) != 3 {
  41. continue
  42. }
  43. ret[array[0]] = true
  44. }
  45. return ret
  46. }
  47. func GetAcounts(filename string) ([][]string, error) {
  48. visited := GetRecord("record.txt")
  49. fi, err := os.Open(filename)
  50. if err != nil {
  51. return nil, err
  52. }
  53. defer fi.Close()
  54. ret := [][]string{}
  55. br := bufio.NewReader(fi)
  56. for {
  57. a, _, c := br.ReadLine()
  58. if c == io.EOF {
  59. break
  60. }
  61. array := strings.Split(string(a), ";")
  62. if len(array) != 3 {
  63. continue
  64. }
  65. if visited[array[0]] {
  66. //fmt.Printf("visited:%v\n", array[2])
  67. continue
  68. }
  69. item := []string{array[0], array[1], array[2]}
  70. ret = append(ret, item)
  71. }
  72. return ret, nil
  73. }
  74. func Displayed(by, elementName string) func(selenium.WebDriver) (bool, error) {
  75. return func(wd selenium.WebDriver) (ok bool, err error) {
  76. var el selenium.WebElement
  77. el, err = wd.FindElement(by, elementName)
  78. if err != nil {
  79. ok = false
  80. err = nil
  81. return
  82. }
  83. ok, err = el.IsDisplayed()
  84. return
  85. }
  86. }
  87. func Check415Error(wd selenium.WebDriver) error {
  88. e, err := wd.FindElement(selenium.ByTagName, "body")
  89. if err != nil {
  90. return err
  91. }
  92. text, _ := e.Text()
  93. str := "错误代码:415"
  94. if len(text) <= len(str) {
  95. return nil
  96. }
  97. if text[:len(str)] == str {
  98. return errors.New("访问受限")
  99. }
  100. return nil
  101. }
  102. func codeGet(data []byte, account string, token string) (bool, string) {
  103. name := account + ".png"
  104. ioutil.WriteFile(name, data, 0666)
  105. defer func() {
  106. os.Remove(name)
  107. }()
  108. bodyBuffer := &bytes.Buffer{}
  109. bodyWriter := multipart.NewWriter(bodyBuffer)
  110. bodyWriter.WriteField("token", token)
  111. bodyWriter.WriteField("type", "local")
  112. fileWriter, _ := bodyWriter.CreateFormFile("file", name)
  113. file, _ := os.Open(name)
  114. defer file.Close()
  115. io.Copy(fileWriter, file)
  116. contentType := bodyWriter.FormDataContentType()
  117. bodyWriter.Close()
  118. resp, err := http.Post("http://www.bhshare.cn/imgcode/", contentType, bodyBuffer)
  119. fmt.Println("验证码识别 err :", err)
  120. defer resp.Body.Close()
  121. resp_body, _ := ioutil.ReadAll(resp.Body)
  122. defer resp.Body.Close()
  123. fmt.Printf("%s\n", resp_body)
  124. timesExist := gjson.GetBytes(resp_body, "times").Exists()
  125. //fmt.Printf("%v,%v\n", gjson.GetBytes(resp_body, "times").Exists(),gjson.GetBytes(resp_body, "times").Int())
  126. if !timesExist {
  127. return false, ""
  128. }
  129. times := gjson.GetBytes(resp_body, "times").Int()
  130. if times < 1 {
  131. return true, ""
  132. }
  133. return false, gjson.GetBytes(resp_body, "data").String()
  134. }
  135. var dbMutex sync.Mutex
  136. var db *leveldb.DB
  137. func TokenDbOpen() {
  138. dbMutex.Lock()
  139. defer dbMutex.Unlock()
  140. if db != nil {
  141. return
  142. }
  143. db, _ = leveldb.OpenFile("tokens", nil)
  144. }
  145. func TokenDbClear() {
  146. dbMutex.Lock()
  147. defer dbMutex.Unlock()
  148. if db == nil {
  149. return
  150. }
  151. for _, v := range config.Conf.CodeTokens {
  152. _ = db.Delete([]byte(v), nil)
  153. }
  154. }
  155. func TokenDbClose() {
  156. dbMutex.Lock()
  157. defer dbMutex.Unlock()
  158. if db == nil {
  159. return
  160. }
  161. db.Close()
  162. db = nil
  163. }
  164. func TokenDbGet(token string) string {
  165. dbMutex.Lock()
  166. defer dbMutex.Unlock()
  167. if db == nil {
  168. return ""
  169. }
  170. bytes, _ := db.Get([]byte(token), nil)
  171. return string(bytes)
  172. }
  173. func TokenDbPut(token string, value string) {
  174. dbMutex.Lock()
  175. defer dbMutex.Unlock()
  176. if db == nil {
  177. return
  178. }
  179. _ = db.Put([]byte(token), []byte(value), nil)
  180. }
  181. func codeGetHandle(data []byte, account string, tokens []string) string {
  182. now := time.Now()
  183. day := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
  184. dayStr := fmt.Sprintf("%d", day.Unix())
  185. for _, token := range tokens {
  186. if db != nil {
  187. str := TokenDbGet(token)
  188. if str == dayStr {
  189. continue
  190. }
  191. }
  192. timesIsLimit, code := codeGet(data, account, token)
  193. if timesIsLimit {
  194. TokenDbPut(token, dayStr)
  195. }
  196. if code != "" && code[0] >= '0' && code[0] <= '9' {
  197. return code
  198. }
  199. }
  200. return ""
  201. }
  202. func StartOneStudent(usernameText string, passwordText string, realName string) (service *selenium.Service, wd selenium.WebDriver, err error) {
  203. defer func() {
  204. if r := recover(); r != nil {
  205. err = errors.New(fmt.Sprintf("panic error %v-%v", err, r))
  206. }
  207. }()
  208. service, wd, err = StartOneStudentHandle(usernameText, passwordText, usernameText)
  209. return service, wd, err
  210. }
  211. func nextPage(wd selenium.WebDriver) []selenium.WebElement {
  212. next, err := wd.FindElement(selenium.ByXPATH, "//button[@class='btn-next']")
  213. if err != nil {
  214. return nil
  215. }
  216. disable, _ := next.GetAttribute("disabled")
  217. fmt.Printf("disable:%v\n", disable)
  218. if disable == "disabled" || disable == "true" {
  219. return nil
  220. }
  221. tryClick(wd, next)
  222. time.Sleep(5 * time.Second)
  223. buttons, _ := wd.FindElements(selenium.ByXPATH, "//a[text()='去学习']")
  224. if len(buttons) == 0 {
  225. return nil
  226. }
  227. return buttons
  228. }
  229. func tryChangePassSure(wd selenium.WebDriver) bool {
  230. act, err := wd.ActiveElement()
  231. if err != nil {
  232. fmt.Printf("find active element\n")
  233. return false
  234. }
  235. _, err = act.FindElement(selenium.ByXPATH, "//div[contains(text(), '请尽快修改密码')]")
  236. if err != nil {
  237. fmt.Printf("find active element to change pass\n")
  238. return false
  239. }
  240. sure, err := act.FindElement(selenium.ByXPATH, "//span[text()='确定']")
  241. if err != nil {
  242. fmt.Printf("find active element to change pass sure\n")
  243. return false
  244. }
  245. err = tryClick(wd, sure)
  246. if err != nil {
  247. fmt.Printf("find active element to click\n")
  248. return false
  249. }
  250. return true
  251. }
  252. func StartOneStudentHandle(usernameText string, passwordText string, realName string) (*selenium.Service, selenium.WebDriver, error) {
  253. var (
  254. //driverPath = `C:\Users\lqzlt\AppData\Local\Google\Chrome\Application\chromedriver.exe`
  255. driverPath = `chromedriver.exe`
  256. port = 60002
  257. )
  258. // chrome服务
  259. service, err := selenium.NewChromeDriverService(driverPath, port, []selenium.ServiceOption{}...)
  260. if nil != err {
  261. return nil, nil, err
  262. }
  263. defer func() {
  264. //_ = service.Stop()
  265. }()
  266. // Connect to the WebDriver instance running locally.
  267. caps := selenium.Capabilities{"browserName": "chrome"}
  268. imagCaps := map[string]interface{}{
  269. //"profile.managed_default_content_settings.images": 2,
  270. //"profile.default_content_setting_values":2,
  271. "same-site-by-default-cookies": 2,
  272. "cookies-without-same-site-must-be-secure": 2,
  273. }
  274. chromeCaps := chrome.Capabilities{
  275. Prefs: imagCaps,
  276. Path: "",
  277. ExcludeSwitches: []string{"enable-automation"},
  278. //Extensions:[]string{"D:\\sql2struct"},
  279. Args: []string{
  280. //"--headless",
  281. //"--disable-javascript",
  282. "--disable-blink-features=AutomationControlled",
  283. "--no-sandbox",
  284. "--disable-gpu-sandbox",
  285. "--accept-Encoding=gzip, deflate",
  286. "--accept-language=zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6",
  287. "--cache-control=max-age=0",
  288. "--abc=d",
  289. "--load-extention=D:\\sql2struct",
  290. //"--cookie=MoodleSession=66925citrm0o6b68gsfn9quaof;username=2051101450211;UserName=2051101450211",
  291. "--moodleSession=66925citrm0o6b68gsfn9quaof;username=2051101450211",
  292. "--upgrade-insecure-requests=1",
  293. //"--authorization=Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkYyREVCM0ZGMURBMUFEODNFOTA5QjIxMjM4QTZCMjU5RjAyQjNGMDEiLCJ0eXAiOiJKV1QiLCJ4NXQiOiI4dDZ6X3gyaHJZUHBDYklTT0theVdmQXJQd0UifQ.eyJuYmYiOjE2MzYwMTAzNTMsImV4cCI6MTYzNjAxMzk1MywiaXNzIjoiaHR0cDovL3Bhc3Nwb3J0Lm91Y2huLmNuIiwiYXVkIjpbImh0dHA6Ly9wYXNzcG9ydC5vdWNobi5jbi9yZXNvdXJjZXMiLCJvdWNobnN0dWRlbnRzcGFjZWFwaSJdLCJjbGllbnRfaWQiOiJzdHVkZW50c3BhY2UiLCJzdWIiOiJlZjkzZTdlYi0zYjhlLTQ2ZTUtYjA1ZC1mYTJmYjAwYjA2ZmUiLCJhdXRoX3RpbWUiOjE2MzYwMTAyMDMsImlkcCI6ImxvY2FsIiwibmFtZSI6IjIwNTExMDE0NTAyMTEiLCJVc2VySWQiOiJlZjkzZTdlYi0zYjhlLTQ2ZTUtYjA1ZC1mYTJmYjAwYjA2ZmUiLCJVc2VyTmFtZSI6IjIwNTExMDE0NTAyMTEiLCJuaWNrbmFtZSI6IueOi-aVjyIsIkF2YXRhciI6IiIsIlVzZXJUeXBlIjoiMiIsIlByb3ZpbmNlU2Nob29sQ29kZSI6IjUxMSIsIk9yZ2FuaXphdGlvbkNvZGUiOiI1MTE1NDAwIiwiTW9iaWxlQ29uZmlybWVkIjoiRmFsc2UiLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwib3VjaG51c2VyIiwib3VjaG5zdHVkZW50c3BhY2VhcGkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsicHdkIl19.ACwdaY50NnuXiQso4T6dS1PoQc7K2rTBRKWz0VmocC40xL4PlPy3Usf3kAZf-1dNFnh8PTBUWc9s_F6eTp_Rcemu5IBGL-iKs9I-QMeG3u7TnaNbCL4XwUt8vZoOPHaXzor3gN7WEqs01Qj11nTbERZ2ixEMhk2Ov_lGqCS8me67pyvd2HWCP7yUcizfEk4_5FQBZZ12daRpXrBksCC27I8BcpXQLrogWewa1W4x5uhLFeW7BKhBGxYDT4yM-b0RNM2jNTxn-ToqoOgBIsLsnU8e0eOKi6WBuhbDYK8psiCPt1hT_3BumeuJ-P36iuaPLcNv7D2T1WlCMHJe1Fv92Q",
  294. "--connection=keep-alive",
  295. //"--host=chengdu.ouchn.cn",
  296. "--accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
  297. "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36",
  298. },
  299. }
  300. //以上是设置浏览器参数
  301. caps.AddChrome(chromeCaps)
  302. wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
  303. if err != nil {
  304. return service, nil, errors.New(fmt.Sprintf("connect to the webDriver failed:%v\n", err))
  305. }
  306. defer func() {
  307. //_ = wd.Close()
  308. }()
  309. //wd.Get("file:///C:/Users/lqzlt/Desktop/1.html")
  310. //time.Sleep(1*time.Second)
  311. //handleQuestionWebTest(wd)
  312. //time.Sleep(1000*time.Second)
  313. //return service, wd, nil
  314. //err = wd.Get("https://menhu.pt.ouchn.cn/")
  315. err = wd.Get("https://iam.pt.ouchn.cn/am/UI/Login?realm=%2F&service=initService&goto=https%3A%2F%2Fiam.pt.ouchn.cn%2Fam%2Foauth2%2Fauthorize%3Fservice%3DinitService%26response_type%3Dcode%26client_id%3D345fcbaf076a4f8a%26scope%3Dall%26redirect_uri%3Dhttps%253A%252F%252Fmenhu.pt.ouchn.cn%252Fouchnapp%252Fwap%252Flogin%252Findex%26decision%3DAllow")
  316. if err != nil {
  317. return service, wd, err
  318. }
  319. if config.Conf.Timeout == 0 {
  320. config.Conf.Timeout = 20
  321. }
  322. if config.Conf.QuestionCount == 0 {
  323. config.Conf.QuestionCount = 2
  324. }
  325. //time.Sleep(2 * time.Second)
  326. fmt.Printf("查找登录按钮\n")
  327. // 进入登录页面
  328. err = wd.WaitWithTimeout(Displayed(selenium.ByID, "button"), time.Duration(config.Conf.Timeout)*time.Second)
  329. if err != nil {
  330. return service, wd, errors.New(fmt.Sprintf("获取登录页面失败:%v\n", err))
  331. }
  332. fmt.Printf("开始获取用户名按钮\n")
  333. username, err := wd.FindElement(selenium.ByID, "loginName")
  334. if err != nil {
  335. return service, wd, errors.New(fmt.Sprintf("获取用户名按钮:%v\n", err))
  336. }
  337. fmt.Printf("开始获取密码按钮\n")
  338. password, err := wd.FindElement(selenium.ByID, "password")
  339. if err != nil {
  340. return service, wd, errors.New(fmt.Sprintf("获取密码按钮:%v\n", err))
  341. }
  342. fmt.Printf("开始获取登录按钮\n")
  343. submit, err := wd.FindElement(selenium.ByID, "button")
  344. if err != nil {
  345. return service, wd, errors.New(fmt.Sprintf("获取登录按钮:%v\n", err))
  346. }
  347. err = username.SendKeys(usernameText)
  348. if err != nil {
  349. return service, wd, errors.New(fmt.Sprintf("填充用户名:%v\n", err))
  350. }
  351. err = password.SendKeys(passwordText)
  352. if err != nil {
  353. return service, wd, errors.New(fmt.Sprintf("填充密码:%v\n", err))
  354. }
  355. fmt.Printf("开始获取验证码按钮\n")
  356. verifyCode, err := wd.FindElement(selenium.ByID, "validateCode")
  357. if err != nil {
  358. return service, wd, errors.New(fmt.Sprintf("获取验证码按钮失败:%v\n", err))
  359. }
  360. if config.Conf.CodeManual {
  361. count := 0
  362. for count < 60 {
  363. time.Sleep(1 * time.Second)
  364. value, _ := verifyCode.GetAttribute("value")
  365. if len(value) < 4 {
  366. continue
  367. }
  368. break
  369. }
  370. if count >= 60 {
  371. return service, wd, errors.New("未输入验证码")
  372. }
  373. } else {
  374. image, err := wd.FindElement(selenium.ByXPATH, "//img[@id='kaptchaImage']")
  375. if err != nil {
  376. return service, wd, errors.New("验证码获取失败")
  377. }
  378. data, _ := image.Screenshot(false)
  379. code := codeGetHandle(data, usernameText, config.Conf.CodeTokens)
  380. if code == "" {
  381. return service, wd, errors.New("验证码识别失败\n")
  382. }
  383. err = verifyCode.SendKeys(code)
  384. if err != nil {
  385. return service, wd, errors.New(fmt.Sprintf("填充验证码:%v\n", err))
  386. }
  387. }
  388. //err = submit.Click()
  389. err = tryClick(wd, submit)
  390. if err != nil {
  391. return service, wd, errors.New(fmt.Sprintf("点击登录:%v\n", err))
  392. }
  393. //time.Sleep(1200 *time.Second)
  394. //return service, wd, errors.New("登录测试")
  395. time.Sleep(2 * time.Second)
  396. tryChangePassSure(wd)
  397. // 进入学生主页
  398. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//a[text()='去学习']"), time.Duration(config.Conf.Timeout)*time.Second)
  399. if err != nil {
  400. if !tryChangePassSure(wd) {
  401. return service, wd, errors.New(fmt.Sprintf("进入学生主页失败:%v\n", err))
  402. }
  403. time.Sleep(2 * time.Second)
  404. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//a[text()='去学习']"), time.Duration(config.Conf.Timeout)*time.Second)
  405. if err != nil {
  406. fmt.Printf("find active element to find page\n")
  407. return service, wd, errors.New(fmt.Sprintf("进入学生主页失败:%v\n", err))
  408. }
  409. }
  410. fmt.Printf("进入学生主页\n")
  411. buttons, _ := wd.FindElements(selenium.ByXPATH, "//a[text()='去学习']")
  412. currentWindow, err := wd.CurrentWindowHandle()
  413. if err != nil {
  414. return service, wd, errors.New(fmt.Sprintf("获取当前窗口失败:%v\n", err))
  415. }
  416. for buttons != nil {
  417. for i := 0; i < len(buttons); i++ {
  418. button := buttons[i]
  419. text, _ := button.Text()
  420. if text != "去学习" {
  421. continue
  422. }
  423. if true {
  424. ptag, err := button.FindElement(selenium.ByXPATH, "./preceding-sibling::div/p")
  425. if err != nil {
  426. fmt.Printf("查找课程失败:%v\n", err)
  427. return service, wd, err
  428. }
  429. txt, _ := ptag.Text()
  430. array := strings.Split(txt, " ")
  431. if len(array) > 1 {
  432. txt = array[1]
  433. }
  434. txt = strings.TrimSpace(txt)
  435. if !checkCourseExist(config.Conf.Courses, txt) {
  436. fmt.Println("不执行课程:", txt)
  437. continue
  438. }
  439. fmt.Println("要执行课程:", txt)
  440. }
  441. //button.Click()
  442. tryClick(wd, button)
  443. //time.Sleep(60*time.Second)
  444. count := 0
  445. handled := ""
  446. // 等待新页面弹出
  447. for count < 5 {
  448. time.Sleep(1 * time.Second)
  449. handles, _ := wd.WindowHandles()
  450. if len(handles) > 1 {
  451. handled = handles[len(handles)-1]
  452. //
  453. wd.SwitchWindow(handled)
  454. break
  455. }
  456. count++
  457. }
  458. if count > 5 {
  459. continue
  460. }
  461. if err := Check415Error(wd); err != nil {
  462. return service, wd, err
  463. }
  464. // 处理课程
  465. //time.Sleep(4000 * time.Second)
  466. err = CourseHandle(wd, realName)
  467. if err != nil {
  468. return service, wd, err
  469. }
  470. //fmt.Printf("xxxxerr:%v\n", err)
  471. // 关闭当前课程窗口
  472. wd.CloseWindow(handled)
  473. // 切换到学生主页窗口(有进入学习按钮的窗口)
  474. wd.SwitchWindow(currentWindow)
  475. runtime.GC()
  476. }
  477. buttons = nextPage(wd)
  478. }
  479. return service, wd, err
  480. }
  481. // 等待url变化
  482. func waitUrl(wd selenium.WebDriver, url1 string, url2 string, count int) string {
  483. for count >= 0 {
  484. time.Sleep(100 * time.Millisecond)
  485. url, _ := wd.CurrentURL()
  486. if url != url1 && url != url2 {
  487. return url
  488. }
  489. count--
  490. }
  491. return ""
  492. }
  493. func getCookies(wd selenium.WebDriver) (string, error) {
  494. cookies, err := wd.GetCookies()
  495. if err != nil {
  496. return "", errors.New(fmt.Sprintf("获取cookie 失败,%v\n", err))
  497. }
  498. if len(cookies) == 0 {
  499. return "", errors.New(fmt.Sprintf("获取cookie 失败,%v\n", "cookie为空"))
  500. }
  501. ret := ""
  502. for _, v := range cookies {
  503. item := fmt.Sprintf("%s=%s", v.Name, v.Value)
  504. if ret == "" {
  505. ret = item
  506. continue
  507. }
  508. ret = ret + ";"
  509. ret = ret + item
  510. }
  511. return ret, nil
  512. }
  513. func checkCourseExist(courses []string, course string) bool {
  514. for _, v := range courses {
  515. if v == course {
  516. return true
  517. }
  518. }
  519. return false
  520. }
  521. type ElementInfo struct {
  522. Element selenium.WebElement
  523. }
  524. type AnswerInfo struct {
  525. Text string
  526. Radio *ElementInfo
  527. Ptag *ElementInfo
  528. }
  529. type QuestionInfo struct {
  530. Text string
  531. Tag int
  532. Number string
  533. AnswerArray []*AnswerInfo
  534. ChoosedAnswer []int
  535. }
  536. func GenerateRandom(length int) int {
  537. time.Sleep(1 * time.Microsecond)
  538. rand.Seed(time.Now().UnixNano())
  539. return rand.Intn(length)
  540. }
  541. // 计算题
  542. func shortAnswer(question QuestionInfo, questionM map[string][]string) error {
  543. if len(question.AnswerArray) == 0 {
  544. return errors.New(fmt.Sprintf("%s 选项为空", question.Text))
  545. }
  546. str := TrimSpace(question.Text)
  547. str = ParseHan(str)
  548. str = fmt.Sprintf("%d-", question.Tag) + str
  549. answerstrs, _ := questionM[str]
  550. if len(answerstrs) == 0 {
  551. str = strings.TrimRight(str, "分")
  552. answerstrs, _ = questionM[str]
  553. if len(answerstrs) == 0 {
  554. return errors.New("没有答案")
  555. }
  556. }
  557. index := GenerateRandom(len(answerstrs))
  558. question.AnswerArray[0].Ptag.Element.Clear()
  559. question.AnswerArray[0].Ptag.Element.SendKeys(answerstrs[index])
  560. time.Sleep(500 * time.Millisecond)
  561. return nil
  562. }
  563. func tryClick(wd selenium.WebDriver, element selenium.WebElement) error {
  564. array := []interface{}{element}
  565. _, err := wd.ExecuteScript("arguments[0].click()", array)
  566. if err != nil {
  567. fmt.Printf("按钮点击失败:%v\n", err)
  568. }
  569. return err
  570. }
  571. var questionTagM = map[int]string{
  572. 1: "单选题",
  573. 2: "判断题",
  574. 3: "多选题",
  575. 4: "计算题/简答题",
  576. }
  577. func getTagName(tag int) string {
  578. ret := questionTagM[tag]
  579. if ret == "" {
  580. return "未知题型"
  581. }
  582. return ret
  583. }
  584. // 题目号个数-本来就随机的题数, 再随机生成错误个数, 根据错误个数随机选择题号
  585. func getRandErrorNumber(emptyCount int, originNumber map[int]bool, maxNumber int) map[int]bool {
  586. questionCount := len(originNumber) - emptyCount
  587. if questionCount <= 2 || maxNumber <= 2 {
  588. return map[int]bool{}
  589. }
  590. count := 0
  591. if questionCount < 5 {
  592. count = 1
  593. } else {
  594. rand.Seed(time.Now().UnixNano())
  595. count = rand.Intn(3)
  596. if count == 0 {
  597. count = 1
  598. }
  599. }
  600. m := map[int]bool{}
  601. for len(m) < count {
  602. number := rand.Intn(maxNumber + 1)
  603. if originNumber[number] {
  604. m[number] = true
  605. }
  606. }
  607. return m
  608. }
  609. func random(count int) int {
  610. rand.Seed(time.Now().UnixNano())
  611. return rand.Intn(count)
  612. }
  613. func chooseAnswer2(wd selenium.WebDriver, questions []QuestionInfo, questionM map[string][]string) error {
  614. if config.Conf.NewStyle {
  615. return chooseAnswer2New(wd, questions, questionM)
  616. }
  617. emptyCount := 0
  618. if false {
  619. emptyCount = 3
  620. }
  621. // 选择或判断题目号
  622. originNumber := map[int]bool{}
  623. // 选择或判断最大题目号
  624. maxNumber := 0
  625. // 先收集答案
  626. for i, _ := range questions {
  627. questions[i].ChoosedAnswer = []int{}
  628. fmt.Printf("\n题目类型:%s\n", getTagName(questions[i].Tag))
  629. fmt.Printf("题目:%s\n", questions[i].Text)
  630. for _, v := range questions[i].AnswerArray {
  631. fmt.Printf("选项:%s\n", v.Text)
  632. }
  633. if questions[i].Tag == 4 {
  634. shortAnswer(questions[i], questionM)
  635. continue
  636. }
  637. if len(questions[i].AnswerArray) == 0 {
  638. return errors.New(fmt.Sprintf("%s 选项为空", questions[i].Text))
  639. }
  640. str := TrimSpace(questions[i].Text)
  641. str = ParseHan(str)
  642. //str = trimQuestionPrefix(str)
  643. str = fmt.Sprintf("%d-", questions[i].Tag) + str
  644. answerstrs, _ := questionM[str]
  645. if len(answerstrs) == 0 || answerstrs[0] == "" {
  646. emptyCount++
  647. if emptyCount > config.Conf.QuestionCount {
  648. return errors.New(fmt.Sprintf("%s 题库答案为空", questions[i].Text))
  649. }
  650. fmt.Printf("%s 题库答案为空, 空答案数小于%d,进行随机选择", questions[i].Text, config.Conf.QuestionCount)
  651. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, random(len(questions[i].AnswerArray)))
  652. continue
  653. }
  654. find := 0
  655. // 单选
  656. if questions[i].Tag == 1 {
  657. //answerstrs = answerstrs[:1]
  658. }
  659. if questions[i].Tag == 3 {
  660. //fmt.Printf("*****************************************:%s,%v\n", questions[i].Text,answerstrs)
  661. }
  662. for _, answerstr := range answerstrs {
  663. if answerstr == "" {
  664. continue
  665. }
  666. for j, v := range questions[i].AnswerArray {
  667. if questions[i].Tag == 1 && find > 0 {
  668. break
  669. }
  670. right := false
  671. if questions[i].Tag == 3 {
  672. right = TrimSpace(answerstr) == TrimSpace(v.Text)
  673. } else {
  674. right = tryEqual(answerstr, TrimSpace(v.Text))
  675. }
  676. if right {
  677. fmt.Printf("选择答案:%s\n", answerstr)
  678. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, j)
  679. originNumber[i] = true
  680. maxNumber = i
  681. //ppp, _ := v.Radio.Element.FindElement(selenium.ByXPATH, "./following-sibling::span")
  682. //txt, _ := ppp.Text()
  683. //fmt.Printf("kkkkkkkkkkkkkkkkkkk:%s,%s\n", txt, answerstr)
  684. if questions[i].Tag == 3 {
  685. //time.Sleep(10*time.Second)
  686. }
  687. find++
  688. //time.Sleep(1 * time.Second)
  689. break
  690. }
  691. }
  692. }
  693. if find == 0 {
  694. emptyCount++
  695. if emptyCount > config.Conf.QuestionCount {
  696. return errors.New(fmt.Sprintf("%s 题库答案为空2", questions[i].Text))
  697. }
  698. fmt.Printf("未找到匹配答案,题库中的答案有:%v\n", answerstrs)
  699. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, random(len(questions[i].AnswerArray)))
  700. continue
  701. //return errors.New(fmt.Sprintf("%s 未找到匹配答案", questions[i].Text))
  702. }
  703. }
  704. // 选择答案, 先随机生成需故意填错的错误题号
  705. errorNumber := getRandErrorNumber(emptyCount, originNumber, maxNumber)
  706. defer func() {
  707. errorNumber = nil
  708. }()
  709. for i, _ := range questions {
  710. if len(questions[i].ChoosedAnswer) == 0 {
  711. continue
  712. }
  713. // 不需要故意填错
  714. if !errorNumber[i] {
  715. for _, v := range questions[i].ChoosedAnswer {
  716. err := tryClick(wd, questions[i].AnswerArray[v].Radio.Element)
  717. if err != nil {
  718. fmt.Printf("点击答案错误:%v\n", err)
  719. return err
  720. }
  721. time.Sleep(1 * time.Second)
  722. }
  723. continue
  724. }
  725. // 需要故意填错时,随机选择错误答案
  726. // 正确答案map
  727. cm := map[int]bool{}
  728. for _, j := range questions[i].ChoosedAnswer {
  729. cm[j] = true
  730. }
  731. // 可供选择的错误答案
  732. array := []int{}
  733. for j, _ := range questions[i].AnswerArray {
  734. if !cm[j] {
  735. array = append(array, j)
  736. }
  737. }
  738. // 随机生成索引
  739. arrayIndex := random(len(array))
  740. // 获取错误答案索引
  741. errorIndex := 0
  742. if len(array) > 0 {
  743. errorIndex = array[arrayIndex]
  744. } else {
  745. errorIndex = random(len(questions[i].AnswerArray))
  746. }
  747. err := tryClick(wd, questions[i].AnswerArray[errorIndex].Radio.Element)
  748. if err != nil {
  749. fmt.Printf("点击答案错误:%v\n", err)
  750. return err
  751. }
  752. time.Sleep(1 * time.Second)
  753. }
  754. return nil
  755. }
  756. func chooseAnswer2New(wd selenium.WebDriver, questions []QuestionInfo, questionM map[string][]string) error {
  757. emptyCount := 0
  758. if false {
  759. emptyCount = 3
  760. }
  761. // 选择或判断题目号
  762. originNumber := map[int]bool{}
  763. // 选择或判断最大题目号
  764. maxNumber := 0
  765. // 先收集答案
  766. for i, _ := range questions {
  767. questions[i].ChoosedAnswer = []int{}
  768. fmt.Printf("\n题目类型:%s\n", getTagName(questions[i].Tag))
  769. fmt.Printf("题目:%s\n", questions[i].Text)
  770. for _, v := range questions[i].AnswerArray {
  771. fmt.Printf("选项:%s\n", v.Text)
  772. }
  773. if questions[i].Tag == 4 {
  774. shortAnswer(questions[i], questionM)
  775. continue
  776. }
  777. if len(questions[i].AnswerArray) == 0 {
  778. return errors.New(fmt.Sprintf("%s 选项为空", questions[i].Text))
  779. }
  780. str := TrimSpace(questions[i].Text)
  781. str = ParseHan(str)
  782. //str = trimQuestionPrefix(str)
  783. str = fmt.Sprintf("%d-", questions[i].Tag) + str
  784. answerstrs, _ := questionM[str]
  785. if len(answerstrs) == 0 || answerstrs[0] == "" {
  786. emptyCount++
  787. if emptyCount > config.Conf.QuestionCount {
  788. return errors.New(fmt.Sprintf("%s 题库答案为空", questions[i].Text))
  789. }
  790. fmt.Printf("%s 题库答案为空, 空答案数小于%d,进行随机选择", questions[i].Text, config.Conf.QuestionCount)
  791. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, random(len(questions[i].AnswerArray)))
  792. continue
  793. }
  794. find := 0
  795. // 单选
  796. if questions[i].Tag == 1 {
  797. //answerstrs = answerstrs[:1]
  798. }
  799. if questions[i].Tag == 3 {
  800. //fmt.Printf("*****************************************:%s,%v\n", questions[i].Text,answerstrs)
  801. }
  802. for _, answerstr := range answerstrs {
  803. if answerstr == "" {
  804. continue
  805. }
  806. for j, _ := range questions[i].AnswerArray {
  807. if questions[i].Tag == 1 && find > 0 {
  808. break
  809. }
  810. right := false
  811. if questions[i].Tag == 1 || questions[i].Tag == 3 {
  812. //right = TrimSpace(answerstr) == TrimSpace(v.Text)
  813. right = j == SelectAnswerFlagMap[answerstr]
  814. } else {
  815. //right = tryEqual(answerstr, TrimSpace(v.Text))
  816. right = j == JudgeAnswerMap[answerstr]
  817. }
  818. if right {
  819. fmt.Printf("选择答案:%s\n", answerstr)
  820. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, j)
  821. originNumber[i] = true
  822. maxNumber = i
  823. //ppp, _ := v.Radio.Element.FindElement(selenium.ByXPATH, "./following-sibling::span")
  824. //txt, _ := ppp.Text()
  825. //fmt.Printf("kkkkkkkkkkkkkkkkkkk:%s,%s\n", txt, answerstr)
  826. if questions[i].Tag == 3 {
  827. //time.Sleep(10*time.Second)
  828. }
  829. find++
  830. //time.Sleep(1 * time.Second)
  831. break
  832. }
  833. }
  834. }
  835. if find == 0 {
  836. emptyCount++
  837. if emptyCount > config.Conf.QuestionCount {
  838. return errors.New(fmt.Sprintf("%s 题库答案为空2", questions[i].Text))
  839. }
  840. fmt.Printf("未找到匹配答案,题库中的答案有:%v\n", answerstrs)
  841. questions[i].ChoosedAnswer = append(questions[i].ChoosedAnswer, random(len(questions[i].AnswerArray)))
  842. continue
  843. //return errors.New(fmt.Sprintf("%s 未找到匹配答案", questions[i].Text))
  844. }
  845. }
  846. // 选择答案, 先随机生成需故意填错的错误题号
  847. errorNumber := getRandErrorNumber(emptyCount, originNumber, maxNumber)
  848. defer func() {
  849. errorNumber = nil
  850. }()
  851. for i, _ := range questions {
  852. if len(questions[i].ChoosedAnswer) == 0 {
  853. continue
  854. }
  855. // 不需要故意填错
  856. if !errorNumber[i] {
  857. for _, v := range questions[i].ChoosedAnswer {
  858. err := tryClick(wd, questions[i].AnswerArray[v].Radio.Element)
  859. if err != nil {
  860. fmt.Printf("点击答案错误:%v\n", err)
  861. return err
  862. }
  863. time.Sleep(1 * time.Second)
  864. }
  865. continue
  866. }
  867. // 需要故意填错时,随机选择错误答案
  868. // 正确答案map
  869. cm := map[int]bool{}
  870. for _, j := range questions[i].ChoosedAnswer {
  871. cm[j] = true
  872. }
  873. // 可供选择的错误答案
  874. array := []int{}
  875. for j, _ := range questions[i].AnswerArray {
  876. if !cm[j] {
  877. array = append(array, j)
  878. }
  879. }
  880. // 随机生成索引
  881. arrayIndex := random(len(array))
  882. // 获取错误答案索引
  883. errorIndex := 0
  884. if len(array) > 0 {
  885. errorIndex = array[arrayIndex]
  886. } else {
  887. errorIndex = random(len(questions[i].AnswerArray))
  888. }
  889. err := tryClick(wd, questions[i].AnswerArray[errorIndex].Radio.Element)
  890. if err != nil {
  891. fmt.Printf("点击答案错误:%v\n", err)
  892. return err
  893. }
  894. time.Sleep(1 * time.Second)
  895. }
  896. return nil
  897. }
  898. func chooseAnswer(wd selenium.WebDriver, questions []QuestionInfo, questionM map[string][]string) error {
  899. // 正常选择答案
  900. if !config.Conf.QuestionRandom {
  901. return chooseAnswer2(wd, questions, questionM)
  902. }
  903. // 全部随机选
  904. for i, _ := range questions {
  905. fmt.Printf("\n题目类型:%s\n", getTagName(questions[i].Tag))
  906. fmt.Printf("题目:%s\n", questions[i].Text)
  907. for _, v := range questions[i].AnswerArray {
  908. fmt.Printf("选项:%s\n", v.Text)
  909. }
  910. if questions[i].Tag == 4 {
  911. shortAnswer(questions[i], questionM)
  912. continue
  913. }
  914. if len(questions[i].AnswerArray) == 0 {
  915. return errors.New(fmt.Sprintf("%s 选项为空", questions[i].Text))
  916. }
  917. str := TrimSpace(questions[i].Text)
  918. str = ParseHan(str)
  919. //str = trimQuestionPrefix(str)
  920. str = fmt.Sprintf("%d-", questions[i].Tag) + str
  921. answerstrs, _ := questionM[str]
  922. if len(answerstrs) == 0 || answerstrs[0] == "" {
  923. er := tryClick(wd, questions[i].AnswerArray[random(len(questions[i].AnswerArray))].Radio.Element)
  924. if er != nil {
  925. fmt.Printf("radio click:%v\n", er)
  926. return er
  927. }
  928. time.Sleep(1 * time.Second)
  929. continue
  930. }
  931. find := 0
  932. // 单选
  933. if questions[i].Tag == 1 {
  934. //answerstrs = answerstrs[:1]
  935. }
  936. if questions[i].Tag == 3 {
  937. //fmt.Printf("*****************************************:%s,%v\n", questions[i].Text,answerstrs)
  938. }
  939. for _, answerstr := range answerstrs {
  940. if answerstr == "" {
  941. continue
  942. }
  943. for _, v := range questions[i].AnswerArray {
  944. if questions[i].Tag == 1 && find > 0 {
  945. break
  946. }
  947. right := false
  948. if questions[i].Tag == 3 {
  949. right = TrimSpace(answerstr) == TrimSpace(v.Text)
  950. } else {
  951. right = tryEqual(answerstr, TrimSpace(v.Text))
  952. }
  953. if right {
  954. fmt.Printf("选择答案:%s\n", answerstr)
  955. er := tryClick(wd, v.Radio.Element)
  956. if er != nil {
  957. fmt.Printf("radio click:%v\n", er)
  958. return er
  959. }
  960. //ppp, _ := v.Radio.Element.FindElement(selenium.ByXPATH, "./following-sibling::span")
  961. //txt, _ := ppp.Text()
  962. //fmt.Printf("kkkkkkkkkkkkkkkkkkk:%s,%s\n", txt, answerstr)
  963. if questions[i].Tag == 3 {
  964. //time.Sleep(10*time.Second)
  965. }
  966. find++
  967. time.Sleep(1 * time.Second)
  968. break
  969. }
  970. }
  971. }
  972. if find == 0 {
  973. er := tryClick(wd, questions[i].AnswerArray[random(len(questions[i].AnswerArray))].Radio.Element)
  974. if er != nil {
  975. fmt.Printf("radio click:%v\n", er)
  976. return er
  977. }
  978. //fmt.Printf("未找到匹配答案,题库中的答案有:%v\n", answerstrs)
  979. continue
  980. //return errors.New(fmt.Sprintf("%s 未找到匹配答案", questions[i].Text))
  981. }
  982. }
  983. return nil
  984. }
  985. func getCourseId(src string) string {
  986. array := strings.Split(src, "/")
  987. if len(array) < 5 {
  988. return ""
  989. }
  990. return array[4]
  991. }
  992. type CourseModule struct {
  993. Name string
  994. ModuleId string
  995. }
  996. func getUploadIds(moduleId string, cookie string) ([]int64, error) {
  997. url := fmt.Sprintf("https://lms.ouchn.cn/api/activities/%s", moduleId)
  998. request, err := http.NewRequest("GET", url, nil)
  999. if err != nil {
  1000. return nil, err
  1001. }
  1002. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1003. request.Header.Set("Cookie", cookie)
  1004. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1005. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1006. client := http.Client{}
  1007. resp, err := client.Do(request)
  1008. if err != nil {
  1009. return nil, err
  1010. }
  1011. defer resp.Body.Close()
  1012. respBytes, err := ioutil.ReadAll(resp.Body)
  1013. if err != nil {
  1014. return nil, err
  1015. }
  1016. ret := []int64{}
  1017. uploads := gjson.GetBytes(respBytes, "uploads").Array()
  1018. for _, upload := range uploads {
  1019. id := upload.Get("id").Int()
  1020. ret = append(ret, id)
  1021. }
  1022. return ret, nil
  1023. }
  1024. func getCourseModules(courceId string, cookie string, specialCourse bool) ([]CourseModule, error) {
  1025. url := fmt.Sprintf("https://lms.ouchn.cn/api/courses/%s/modules", courceId)
  1026. request, err := http.NewRequest("GET", url, nil)
  1027. if err != nil {
  1028. return nil, err
  1029. }
  1030. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1031. request.Header.Set("Cookie", cookie)
  1032. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1033. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1034. client := http.Client{}
  1035. resp, err := client.Do(request)
  1036. if err != nil {
  1037. return nil, err
  1038. }
  1039. defer resp.Body.Close()
  1040. respBytes, err := ioutil.ReadAll(resp.Body)
  1041. if err != nil {
  1042. return nil, err
  1043. }
  1044. ret := []CourseModule{}
  1045. mos := gjson.GetBytes(respBytes, "modules").Array()
  1046. for _, mo := range mos {
  1047. if (len(mo.Get("syllabuses").Array())) == 0 && !specialCourse {
  1048. continue
  1049. }
  1050. item := CourseModule{}
  1051. item.ModuleId = fmt.Sprintf("%d", mo.Get("id").Int())
  1052. item.Name = mo.Get("name").String()
  1053. ret = append(ret, item)
  1054. }
  1055. return ret, nil
  1056. }
  1057. type ModuleSubInfo struct {
  1058. Name string
  1059. Id string
  1060. Stype string
  1061. SyllabusId int64
  1062. }
  1063. type ModuleInfo struct {
  1064. TxtInfos []ModuleSubInfo
  1065. ExamInfos []ModuleSubInfo
  1066. VedioInfos []ModuleSubInfo
  1067. }
  1068. func GetModuleInfo(courseId string, moduleId string, cookie string) (*ModuleInfo, error) {
  1069. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/%s"+
  1070. "/all-activities?module_ids=[%s]"+
  1071. "&activity_types=learning_activities,exams,classrooms,"+
  1072. "live_records,rollcalls&no-loading-animation=true", courseId, moduleId)
  1073. request, err := http.NewRequest("GET", url, nil)
  1074. if err != nil {
  1075. return nil, err
  1076. }
  1077. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1078. request.Header.Set("Cookie", cookie)
  1079. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1080. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1081. client := http.Client{Timeout: 60 * time.Second}
  1082. resp, err := client.Do(request)
  1083. if err != nil {
  1084. return nil, err
  1085. }
  1086. defer resp.Body.Close()
  1087. respBytes, err := ioutil.ReadAll(resp.Body)
  1088. if err != nil {
  1089. return nil, err
  1090. }
  1091. ret := &ModuleInfo{}
  1092. // 试题
  1093. exams := []ModuleSubInfo{}
  1094. examsArray := gjson.GetBytes(respBytes, "exams").Array()
  1095. for _, v := range examsArray {
  1096. item := ModuleSubInfo{}
  1097. item.Name = v.Get("title").String()
  1098. unikey := v.Get("unique_key").String()
  1099. array := strings.Split(unikey, "-")
  1100. if len(array) < 2 {
  1101. continue
  1102. }
  1103. item.Id = array[1]
  1104. exams = append(exams, item)
  1105. }
  1106. // 文档和视频
  1107. texts := []ModuleSubInfo{}
  1108. vedios := []ModuleSubInfo{}
  1109. array := gjson.GetBytes(respBytes, "learning_activities").Array()
  1110. for _, v := range array {
  1111. stype := v.Get("type").String()
  1112. syllabusId := v.Get("syllabus_id").Int()
  1113. item := ModuleSubInfo{}
  1114. item.Name = v.Get("title").String()
  1115. item.SyllabusId = syllabusId
  1116. item.Stype = stype
  1117. unikey := v.Get("unique_key").String()
  1118. array := strings.Split(unikey, "-")
  1119. if len(array) < 2 {
  1120. continue
  1121. }
  1122. item.Id = array[1]
  1123. if stype == "page" {
  1124. texts = append(texts, item)
  1125. } else if stype == "online_video" || (stype == "web_link" && strings.Contains(item.Name, "视频")) || stype == "material" {
  1126. index := -1
  1127. for i := len(vedios) - 1; i >= 0; i-- {
  1128. if item.SyllabusId == vedios[i].SyllabusId {
  1129. index = i
  1130. break
  1131. }
  1132. }
  1133. if index == -1 || index == len(vedios)-1 {
  1134. vedios = append(vedios, item)
  1135. } else {
  1136. tmp := []ModuleSubInfo{}
  1137. tmp = append(tmp, vedios[:index+1]...)
  1138. tmp = append(tmp, item)
  1139. tmp = append(tmp, vedios[index+1:]...)
  1140. vedios = tmp
  1141. }
  1142. }
  1143. }
  1144. ret.ExamInfos = exams
  1145. ret.TxtInfos = texts
  1146. ret.VedioInfos = vedios
  1147. return ret, nil
  1148. }
  1149. func MakeTxtUrl(courseId string, moduleId string, id string) string {
  1150. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/%s", courseId, id)
  1151. }
  1152. func MakeExamUrl(courseId string, moduleId string, id string) string {
  1153. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/exam/%s", courseId, id)
  1154. }
  1155. func VedioCompleteHandle(cookie string, id string) error {
  1156. count := 0
  1157. start, end := int64(0), int64(1000)
  1158. for count < 5 {
  1159. finish, err := VedioComplete(cookie, id, start, end)
  1160. if err != nil {
  1161. return err
  1162. }
  1163. if finish < 100 {
  1164. start = end
  1165. end += 1000
  1166. count++
  1167. continue
  1168. }
  1169. return nil
  1170. }
  1171. return errors.New("视频处理失败")
  1172. }
  1173. func VedioComplete(cookie string, id string, start, end int64) (int64, error) {
  1174. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/activities-read/%s", id)
  1175. //fmt.Printf("get sections:%v,%v\n", str, cookie)
  1176. m := map[string]interface{}{
  1177. "start": start,
  1178. "end": end,
  1179. }
  1180. content, _ := json.Marshal(m)
  1181. reader := bytes.NewReader(content)
  1182. fmt.Printf("post data:%s\n", content)
  1183. request, err := http.NewRequest("POST", url, reader)
  1184. if err != nil {
  1185. return 0, err
  1186. }
  1187. request.Header.Set("Content-Type", "application/json")
  1188. request.Header.Set("Cookie", cookie)
  1189. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1190. client := http.Client{}
  1191. resp, err := client.Do(request)
  1192. if err != nil {
  1193. return 0, err
  1194. }
  1195. defer resp.Body.Close()
  1196. respBytes, err := ioutil.ReadAll(resp.Body)
  1197. if err != nil {
  1198. return 0, err
  1199. }
  1200. fmt.Printf("get bytes:%s\n", respBytes)
  1201. if gjson.GetBytes(respBytes, "activity_id").Int() == 0 {
  1202. return 0, errors.New("视频处理失败")
  1203. }
  1204. str := gjson.GetBytes(respBytes, "data").Get("completeness").String()
  1205. if str == "full" {
  1206. return 100, nil
  1207. }
  1208. if str == "part" {
  1209. return 10, nil
  1210. }
  1211. return gjson.GetBytes(respBytes, "data").Get("completeness").Int(), nil
  1212. }
  1213. func examHandleJump(wd selenium.WebDriver, courseId, moduleId, id string) error {
  1214. curl, _ := wd.CurrentURL()
  1215. fmt.Printf("url1:%s\n", curl)
  1216. url := MakeExamUrl(courseId, moduleId, id)
  1217. wd.Get(url)
  1218. time.Sleep(2 * time.Second)
  1219. curl, _ = wd.CurrentURL()
  1220. fmt.Printf("url2:%s\n", curl)
  1221. return nil
  1222. }
  1223. func examHandleStart(wd selenium.WebDriver) error {
  1224. // 点击答题按钮
  1225. err := wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//a[@class='button button-green take-exam ng-scope']"), time.Duration(config.Conf.Timeout)*time.Second)
  1226. if err != nil && strings.Contains(err.Error(), "timeout") {
  1227. curl, _ := wd.CurrentURL()
  1228. fmt.Printf("再次尝试:%s\n", curl)
  1229. wd.Get(curl)
  1230. time.Sleep(2 * time.Second)
  1231. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//a[@class='button button-green take-exam ng-scope']"), time.Duration(config.Conf.Timeout)*time.Second)
  1232. }
  1233. if err != nil {
  1234. return err
  1235. }
  1236. button, err := wd.FindElement(selenium.ByXPATH, "//a[@class='button button-green take-exam ng-scope']")
  1237. if err != nil {
  1238. return err
  1239. }
  1240. //button.Click()
  1241. tryClick(wd, button)
  1242. fmt.Printf("点击答题按钮\n")
  1243. time.Sleep(2 * time.Second)
  1244. active, _ := wd.ActiveElement()
  1245. // 勾选同意框
  1246. chbox, err := active.FindElement(selenium.ByXPATH, "//input[@type='checkbox' and @name='confirm']")
  1247. if err != nil {
  1248. fmt.Printf("未找到同意勾选框:%v\n", err)
  1249. return err
  1250. }
  1251. //err = chbox.Click()
  1252. err = tryClick(wd, chbox)
  1253. if err != nil {
  1254. fmt.Printf("同意勾选框:%v\n", err)
  1255. return err
  1256. }
  1257. fmt.Printf("勾选同意框\n")
  1258. time.Sleep(1 * time.Second)
  1259. // 点击答题
  1260. button, err = active.FindElement(selenium.ByXPATH, "//button[text()='开始答题' or text()='继续答题']")
  1261. if err != nil {
  1262. fmt.Printf("未找到开始答题按钮:%v\n", err)
  1263. return err
  1264. }
  1265. //err = button.Click()
  1266. err = tryClick(wd, button)
  1267. if err != nil {
  1268. fmt.Printf("开始答题按钮:%v\n", err)
  1269. return err
  1270. }
  1271. fmt.Printf("弹窗中点击答题按钮\n")
  1272. time.Sleep(3 * time.Second)
  1273. return nil
  1274. }
  1275. func examHandleSimpleAnswer(wd selenium.WebDriver) ([]QuestionInfo, error) {
  1276. p, err := wd.FindElement(selenium.ByXPATH, "//div[@class='exam-subjects']/ol[@class='subjects-jit-display']")
  1277. if err != nil {
  1278. return nil, err
  1279. }
  1280. path := fmt.Sprintf("./following-sibling::li[@class='subject ng-scope short_answer']")
  1281. ret := []QuestionInfo{}
  1282. count := 0
  1283. for {
  1284. if count == 0 {
  1285. path = fmt.Sprintf("./li[@class='subject ng-scope short_answer']")
  1286. }
  1287. p, err = p.FindElement(selenium.ByXPATH, path)
  1288. if err != nil {
  1289. break
  1290. }
  1291. div, err := p.FindElements(selenium.ByXPATH, "./div/div/div/span/p")
  1292. if err != nil {
  1293. fmt.Printf("简答题目:%v\n", err)
  1294. break
  1295. }
  1296. txt, err := div[len(div)-1].Text()
  1297. fmt.Printf("简答题:%s,%v\n", txt, err)
  1298. item := QuestionInfo{Text: txt, Tag: 4}
  1299. ptag, err := p.FindElement(selenium.ByXPATH, "./div/div/div/div/div/div/div/p")
  1300. if err != nil {
  1301. fmt.Printf("简答题框未找到:%v\n", err)
  1302. break
  1303. }
  1304. if false {
  1305. ptag.Clear()
  1306. err = ptag.SendKeys("test test")
  1307. fmt.Printf("sendkeys error:%v\n", err)
  1308. }
  1309. anserItem := AnswerInfo{Ptag: &ElementInfo{Element: ptag}}
  1310. item.AnswerArray = append(item.AnswerArray, &anserItem)
  1311. ret = append(ret, item)
  1312. count++
  1313. }
  1314. if len(ret) == 0 {
  1315. return nil, errors.New("未找到题")
  1316. }
  1317. //fmt.Printf("yyyyyyyy:%v\n", ret)
  1318. return ret, nil
  1319. }
  1320. func examHandleSpecialQuestion(wd selenium.WebDriver) ([]QuestionInfo, error) {
  1321. fmt.Printf("正在搜集题目和选项请等待\n")
  1322. err := wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//div[@class='exam-subjects']/ol[@class='subjects-jit-display']"), time.Duration(config.Conf.Timeout)*time.Second)
  1323. if err != nil {
  1324. return nil, err
  1325. }
  1326. ol, err := wd.FindElement(selenium.ByXPATH, "//div[@class='exam-subjects']/ol[@class='subjects-jit-display']")
  1327. if err != nil {
  1328. return nil, err
  1329. }
  1330. time.Sleep(2 * time.Second)
  1331. lis1, _ := ol.FindElements(selenium.ByXPATH, "./li")
  1332. if len(lis1) == 0 {
  1333. return nil, errors.New("未找到题")
  1334. }
  1335. ret := []QuestionInfo{}
  1336. for _, li1 := range lis1 {
  1337. attr, _ := li1.GetAttribute("class")
  1338. tag := 0
  1339. if strings.Contains(attr, "true_or_false") {
  1340. tag = 2
  1341. }
  1342. if strings.Contains(attr, "single_selection") {
  1343. tag = 1
  1344. }
  1345. if strings.Contains(attr, "multiple_selection") {
  1346. tag = 3
  1347. }
  1348. if tag == 0 {
  1349. continue
  1350. }
  1351. item := QuestionInfo{Tag: tag}
  1352. p, err := li1.FindElement(selenium.ByXPATH, "./div/div/div/span/p")
  1353. if err != nil {
  1354. return nil, err
  1355. }
  1356. txt, _ := p.Text()
  1357. item.Text = trimQuestionPrefix(txt)
  1358. // 题目选项
  1359. ol, _ := li1.FindElement(selenium.ByXPATH, "./div/div/ol")
  1360. lis, _ := ol.FindElements(selenium.ByXPATH, "./li")
  1361. for _, li := range lis {
  1362. answerIterm := AnswerInfo{}
  1363. if tag == 2 {
  1364. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1365. if err != nil {
  1366. return nil, err
  1367. }
  1368. answerIterm.Radio = &ElementInfo{Element: radio}
  1369. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span")
  1370. if err != nil {
  1371. return nil, err
  1372. }
  1373. txt, _ := answer.Text()
  1374. answerIterm.Text = txt
  1375. //fmt.Printf("收集题目答案:%s\n", txt)
  1376. } else if tag == 1 || tag == 3 {
  1377. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1378. if err != nil {
  1379. return nil, err
  1380. }
  1381. answerIterm.Radio = &ElementInfo{Element: radio}
  1382. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span")
  1383. if err != nil {
  1384. return nil, err
  1385. }
  1386. txt, _ := answer.Text()
  1387. answerIterm.Text = txt
  1388. //fmt.Printf("收集题目答案:%s\n", txt)
  1389. if tag == 3 {
  1390. txt, _ := radio.GetAttribute("class")
  1391. if strings.Contains(txt, "ng-not-empty") {
  1392. //radio.Click()
  1393. tryClick(wd, radio)
  1394. }
  1395. }
  1396. //ppp, _ := radio.FindElement(selenium.ByXPATH, "./following-sibling::span")
  1397. //txt, _ = ppp.Text()
  1398. //fmt.Printf("kkkkkkkkkkkkkkkkkkkaa:%s,%s\n", txt, answerIterm.Text)
  1399. }
  1400. item.AnswerArray = append(item.AnswerArray, &answerIterm)
  1401. }
  1402. ret = append(ret, item)
  1403. }
  1404. return ret, nil
  1405. }
  1406. func trimQuestionPrefix(txt string) string {
  1407. if len(txt) < 2 {
  1408. return txt
  1409. }
  1410. regstr := `^[1-9][0-9]*(\.|、|.)`
  1411. //regstr := `^[1-9][0-9]*、`
  1412. reg := regexp.MustCompile(regstr)
  1413. prefix := reg.FindString(txt)
  1414. return txt[len(prefix):]
  1415. }
  1416. func examHandleQuestion(wd selenium.WebDriver, specialCourse bool) ([]QuestionInfo, error) {
  1417. if specialCourse {
  1418. return examHandleSpecialQuestion(wd)
  1419. }
  1420. err := wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//li[@class='subject ng-scope text']"), time.Duration(config.Conf.Timeout)*time.Second)
  1421. if err != nil {
  1422. return examHandleSimpleAnswer(wd)
  1423. }
  1424. questionClasses, _ := wd.FindElements(selenium.ByXPATH, "//li[@class='subject ng-scope text']")
  1425. ret := []QuestionInfo{}
  1426. for _, v := range questionClasses {
  1427. p, err := v.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='ng-scope']")
  1428. if err != nil {
  1429. return nil, err
  1430. }
  1431. txt, _ := p.Text()
  1432. //fmt.Printf("题类%s\n", txt)
  1433. className := ""
  1434. tag := 0
  1435. // 题目类型
  1436. if strings.Contains(txt, "单项选择") || strings.Contains(txt, "单选") {
  1437. className = "subject ng-scope single_selection"
  1438. tag = 1
  1439. } else if strings.Contains(txt, "判断") {
  1440. className = "subject ng-scope true_or_false"
  1441. tag = 2
  1442. } else if strings.Contains(txt, "多项选择题") || strings.Contains(txt, "多选") {
  1443. tag = 3
  1444. className = "subject ng-scope multiple_selection"
  1445. } else if strings.Contains(txt, "计算题") || strings.Contains(txt, "简答题") {
  1446. tag = 4
  1447. className = "subject ng-scope short_answer"
  1448. }
  1449. if className == "" {
  1450. continue
  1451. }
  1452. p = v
  1453. path := fmt.Sprintf("./following-sibling::li[@class='%s']", className)
  1454. for {
  1455. item := QuestionInfo{Tag: tag}
  1456. // 题目
  1457. p, err = p.FindElement(selenium.ByXPATH, path)
  1458. if err != nil {
  1459. //fmt.Printf("err:%v\n", err)
  1460. break
  1461. }
  1462. x, err := p.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='ng-scope']")
  1463. if err != nil {
  1464. break
  1465. }
  1466. txt, _ := x.Text()
  1467. // 题目描述
  1468. txt = strings.TrimSpace(txt)
  1469. item.Text = trimQuestionPrefix(txt)
  1470. if tag == 4 {
  1471. ptag, err := p.FindElement(selenium.ByXPATH, "./div/div/div/div/div/div/div/p")
  1472. if err != nil {
  1473. fmt.Printf("计算题err:%v\n", err)
  1474. break
  1475. }
  1476. anserItem := &AnswerInfo{Ptag: &ElementInfo{Element: ptag}}
  1477. item.AnswerArray = append(item.AnswerArray, anserItem)
  1478. ret = append(ret, item)
  1479. continue
  1480. }
  1481. // 题目选项
  1482. ol, _ := p.FindElement(selenium.ByXPATH, "./div/div/ol")
  1483. lis, _ := ol.FindElements(selenium.ByXPATH, "./li")
  1484. for _, li := range lis {
  1485. answerIterm := AnswerInfo{}
  1486. if tag == 2 {
  1487. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1488. if err != nil {
  1489. return nil, err
  1490. }
  1491. answerIterm.Radio = &ElementInfo{Element: radio}
  1492. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span")
  1493. if err != nil {
  1494. return nil, err
  1495. }
  1496. txt, _ := answer.Text()
  1497. answerIterm.Text = txt
  1498. } else if tag == 1 || tag == 3 {
  1499. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1500. if err != nil {
  1501. return nil, err
  1502. }
  1503. answerIterm.Radio = &ElementInfo{Element: radio}
  1504. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span/p")
  1505. if err != nil {
  1506. return nil, err
  1507. }
  1508. txt, _ := answer.Text()
  1509. answerIterm.Text = txt
  1510. if tag == 3 {
  1511. txt, _ := radio.GetAttribute("class")
  1512. if strings.Contains(txt, "ng-not-empty") {
  1513. //radio.Click()
  1514. tryClick(wd, radio)
  1515. }
  1516. }
  1517. //ppp, _ := radio.FindElement(selenium.ByXPATH, "./following-sibling::span")
  1518. //txt, _ = ppp.Text()
  1519. //fmt.Printf("kkkkkkkkkkkkkkkkkkkaa:%s,%s\n", txt, answerIterm.Text)
  1520. }
  1521. item.AnswerArray = append(item.AnswerArray, &answerIterm)
  1522. }
  1523. ret = append(ret, item)
  1524. }
  1525. }
  1526. return ret, nil
  1527. }
  1528. func examHandleQuestion2(wd selenium.WebDriver, specialCourse bool) ([]QuestionInfo, error) {
  1529. if specialCourse {
  1530. return examHandleSpecialQuestion(wd)
  1531. }
  1532. fmt.Printf("xxxxxx1\n")
  1533. err := wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//li[contains(@class, 'subject ng-scope short_answer')]"), time.Duration(config.Conf.Timeout)*time.Second)
  1534. if err == nil {
  1535. return examHandleSimpleAnswer(wd)
  1536. }
  1537. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//li[contains(@class, 'subject ng-scope ')]"), time.Duration(config.Conf.Timeout)*time.Second)
  1538. if err != nil {
  1539. return nil, errors.New("未找到题")
  1540. }
  1541. fmt.Printf("xxxx2\n")
  1542. questionClasses, _ := wd.FindElements(selenium.ByXPATH, "//li[contains(@class, 'subject ng-scope ')]")
  1543. ret := []QuestionInfo{}
  1544. fmt.Printf("count:%v\n", len(questionClasses))
  1545. for _, v := range questionClasses {
  1546. attr, _ := v.GetAttribute("class")
  1547. fmt.Printf("attr:%v\n", attr)
  1548. tag := 0
  1549. if strings.Contains(attr, "single_selection") {
  1550. tag = 1
  1551. } else if strings.Contains(attr, "true_or_false") {
  1552. tag = 2
  1553. } else if strings.Contains(attr, "multiple_selection") {
  1554. tag = 3
  1555. } else if strings.Contains(attr, "short_answer") {
  1556. tag = 4
  1557. }
  1558. if tag == 0 {
  1559. continue
  1560. }
  1561. item := QuestionInfo{Tag: tag}
  1562. // 题目
  1563. p := v
  1564. x, err := p.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='ng-scope']")
  1565. if err != nil {
  1566. x, err = p.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='MsoNormal ng-scope']")
  1567. if err != nil {
  1568. break
  1569. }
  1570. }
  1571. txt, _ := x.Text()
  1572. // 题目描述
  1573. txt = strings.TrimSpace(txt)
  1574. item.Text = trimQuestionPrefix(txt)
  1575. fmt.Printf("题目描述:%v\n", txt)
  1576. if tag == 4 {
  1577. ptag, err := p.FindElement(selenium.ByXPATH, "./div/div/div/div/div/div/div/p")
  1578. if err != nil {
  1579. fmt.Printf("计算题err:%v\n", err)
  1580. break
  1581. }
  1582. anserItem := &AnswerInfo{Ptag: &ElementInfo{Element: ptag}}
  1583. item.AnswerArray = append(item.AnswerArray, anserItem)
  1584. ret = append(ret, item)
  1585. continue
  1586. }
  1587. // 题目选项
  1588. ol, _ := p.FindElement(selenium.ByXPATH, "./div/div/ol")
  1589. lis, _ := ol.FindElements(selenium.ByXPATH, "./li")
  1590. for _, li := range lis {
  1591. answerIterm := AnswerInfo{}
  1592. if tag == 2 {
  1593. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1594. if err != nil {
  1595. return nil, err
  1596. }
  1597. answerIterm.Radio = &ElementInfo{Element: radio}
  1598. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span")
  1599. if err != nil {
  1600. return nil, err
  1601. }
  1602. txt, _ := answer.Text()
  1603. answerIterm.Text = txt
  1604. fmt.Printf("选项:%v\n", answerIterm.Text)
  1605. } else if tag == 1 || tag == 3 {
  1606. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  1607. if err != nil {
  1608. return nil, err
  1609. }
  1610. answerIterm.Radio = &ElementInfo{Element: radio}
  1611. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span/p")
  1612. if err != nil {
  1613. return nil, err
  1614. }
  1615. txt, _ := answer.Text()
  1616. answerIterm.Text = txt
  1617. if tag == 3 {
  1618. txt, _ := radio.GetAttribute("class")
  1619. if strings.Contains(txt, "ng-not-empty") {
  1620. //radio.Click()
  1621. tryClick(wd, radio)
  1622. }
  1623. }
  1624. fmt.Printf("选项:%v\n", answerIterm.Text)
  1625. //ppp, _ := radio.FindElement(selenium.ByXPATH, "./following-sibling::span")
  1626. //txt, _ = ppp.Text()
  1627. //fmt.Printf("kkkkkkkkkkkkkkkkkkkaa:%s,%s\n", txt, answerIterm.Text)
  1628. }
  1629. item.AnswerArray = append(item.AnswerArray, &answerIterm)
  1630. }
  1631. ret = append(ret, item)
  1632. }
  1633. return ret, nil
  1634. }
  1635. func examHandleMatch(wd selenium.WebDriver, questions []QuestionInfo, m map[string][]string) error {
  1636. err := chooseAnswer(wd, questions, m)
  1637. if err != nil {
  1638. if false {
  1639. fmt.Printf("err:%v\n", err)
  1640. c := ""
  1641. fmt.Printf("等待输入\n")
  1642. fmt.Scanln(&c)
  1643. m, _ = LoadXlsx(fmt.Sprintf("%s.xlsx", "思想道德修养与法律基础"))
  1644. return examHandleMatch(wd, questions, m)
  1645. }
  1646. return err
  1647. }
  1648. return nil
  1649. }
  1650. func tryEqual(a string, b string) bool {
  1651. if a == b {
  1652. return true
  1653. }
  1654. if !strings.Contains(a, b) {
  1655. return false
  1656. }
  1657. index := strings.Index(a, b)
  1658. for i := 0; i < index; i++ {
  1659. if (a[i] <= 'z' && a[i] >= 'a') || (a[i] >= '0' && a[i] <= '1') || (a[i] <= 'Z' && a[i] >= 'A') {
  1660. return false
  1661. }
  1662. }
  1663. for i := index + len(b); i < len(a); i++ {
  1664. if (a[i] <= 'z' && a[i] >= 'a') || (a[i] >= '0' && a[i] <= '1') || (a[i] <= 'Z' && a[i] >= 'A') {
  1665. return false
  1666. }
  1667. }
  1668. return true
  1669. }
  1670. func examHandleSubmit(wd selenium.WebDriver) error {
  1671. // 交卷
  1672. button1, err := wd.FindElement(selenium.ByXPATH, "//a[@class='button button-green ng-scope' and text()='交卷']")
  1673. if err != nil {
  1674. return err
  1675. }
  1676. //button1.Click()
  1677. tryClick(wd, button1)
  1678. time.Sleep(2 * time.Second)
  1679. // 弹窗中的确定
  1680. active, _ := wd.ActiveElement()
  1681. button2s, err := active.FindElements(selenium.ByXPATH, "//button[text()='确定']")
  1682. if err != nil {
  1683. return err
  1684. }
  1685. for _, button2 := range button2s {
  1686. txt, _ := button2.Text()
  1687. if txt == "确定" {
  1688. //button2.Click()
  1689. tryClick(wd, button2)
  1690. time.Sleep(2 * time.Second)
  1691. return nil
  1692. }
  1693. }
  1694. return errors.New("交卷未找到确定按钮")
  1695. }
  1696. func examHandle(wd selenium.WebDriver, courseId, moduleId, id string, qustionM map[string][]string, specialCourse bool) error {
  1697. cookie, _ := getCookies(wd)
  1698. finsh, submited := checkExamFinish(id, cookie)
  1699. if finsh {
  1700. fmt.Printf("试题达到最大提交次数\n")
  1701. return nil
  1702. }
  1703. if submited {
  1704. fmt.Printf("已完成该试题\n")
  1705. return nil
  1706. }
  1707. // 跳转页面
  1708. if err := examHandleJump(wd, courseId, moduleId, id); err != nil {
  1709. return err
  1710. }
  1711. // 点击答题按钮
  1712. if err := examHandleStart(wd); err != nil {
  1713. return err
  1714. }
  1715. // 收集题目和答案选项
  1716. questions, err := examHandleQuestion2(wd, specialCourse)
  1717. if err != nil {
  1718. fmt.Printf("examhandleQuestion:%v\n")
  1719. if strings.Contains(err.Error(), "未找到题") {
  1720. url, _ := wd.CurrentURL()
  1721. wd.Get(url)
  1722. fmt.Printf("再次尝试:%s\n", url)
  1723. questions, err = examHandleQuestion(wd, specialCourse)
  1724. if err != nil {
  1725. return err
  1726. }
  1727. }
  1728. }
  1729. if true {
  1730. //time.Sleep(4000 * time.Second)
  1731. }
  1732. // 匹配题库并选择答案
  1733. fmt.Printf("*****************开始匹配答案\n")
  1734. if err := examHandleMatch(wd, questions, qustionM); err != nil {
  1735. return err
  1736. }
  1737. fmt.Printf("examHandleMatch:%v\n", err)
  1738. if false {
  1739. //time.Sleep(4000*time.Second)
  1740. fmt.Println("请输入:")
  1741. sal := ""
  1742. fmt.Scanln(&sal)
  1743. //return nil
  1744. }
  1745. // 提交试卷
  1746. fmt.Printf("开始提交\n")
  1747. if config.Conf.SleepTime > 0 {
  1748. fmt.Printf("等待%d秒提交\n", config.Conf.SleepTime)
  1749. time.Sleep(time.Duration(config.Conf.SleepTime) * time.Second)
  1750. }
  1751. err = examHandleSubmit(wd)
  1752. fmt.Printf("examHandleSubmit:%v\n", err)
  1753. if false {
  1754. //time.Sleep(4000*time.Second)
  1755. fmt.Println("请输入2:")
  1756. sal := ""
  1757. fmt.Scanln(&sal)
  1758. }
  1759. return err
  1760. }
  1761. func checkExamFinish(id string, cookie string) (finish bool, submited bool) {
  1762. url := fmt.Sprintf("https://lms.ouchn.cn/api/exams/%s", id)
  1763. request, err := http.NewRequest("GET", url, nil)
  1764. if err != nil {
  1765. return false, false
  1766. }
  1767. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1768. request.Header.Set("Cookie", cookie)
  1769. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1770. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1771. client := http.Client{}
  1772. resp, err := client.Do(request)
  1773. if err != nil {
  1774. return false, false
  1775. }
  1776. defer resp.Body.Close()
  1777. respBytes, err := ioutil.ReadAll(resp.Body)
  1778. if err != nil {
  1779. return false, false
  1780. }
  1781. submitTimes := gjson.GetBytes(respBytes, "submit_times").Int()
  1782. submittedTimes := gjson.GetBytes(respBytes, "submitted_times").Int()
  1783. if submittedTimes > 0 {
  1784. submited = true
  1785. }
  1786. if submitTimes == submittedTimes && submitTimes > 0 {
  1787. return true, submited
  1788. }
  1789. return false, submited
  1790. }
  1791. func isSpecialCourse(title string) bool {
  1792. if title == "斜弱视技术" || title == "常见眼病诊断与处理" || title == "角膜接触镜并发症处理" {
  1793. return true
  1794. }
  1795. for _, v := range config.Conf.SpecialCourses {
  1796. if v == title {
  1797. return true
  1798. }
  1799. }
  1800. return false
  1801. }
  1802. func finishUploadId(uploadId int64, moduleId string, cookie string) error {
  1803. rBody := make(map[string]int64)
  1804. rBody["upload_id"] = uploadId
  1805. rByte, _ := json.Marshal(rBody)
  1806. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/activities-read/%s", moduleId)
  1807. request, err := http.NewRequest("POST", url, bytes.NewBuffer(rByte))
  1808. if err != nil {
  1809. return err
  1810. }
  1811. request.Header.Set("Content-Type", "application/json")
  1812. request.Header.Set("Cookie", cookie)
  1813. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1814. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1815. client := http.Client{}
  1816. resp, err := client.Do(request)
  1817. if err != nil {
  1818. return err
  1819. }
  1820. defer resp.Body.Close()
  1821. respBytes, err := ioutil.ReadAll(resp.Body)
  1822. if err != nil {
  1823. return err
  1824. }
  1825. if true {
  1826. fmt.Println("do material resp:", string(respBytes))
  1827. }
  1828. return nil
  1829. }
  1830. func finishMaterial(uploadIds []int64, moduleId string, cookie string) {
  1831. for _, v := range uploadIds {
  1832. finishUploadId(v, moduleId, cookie)
  1833. time.Sleep(time.Duration(config.Conf.VideoInterval) * time.Second)
  1834. }
  1835. }
  1836. func doMaterial(moduleId string, cookie string) {
  1837. uploadIds, _ := getUploadIds(moduleId, cookie)
  1838. if len(uploadIds) > 0 {
  1839. finishMaterial(uploadIds, moduleId, cookie)
  1840. }
  1841. }
  1842. func CourseHandle(wd selenium.WebDriver, username string) error {
  1843. title, err := wd.Title()
  1844. if title == "" {
  1845. return nil
  1846. }
  1847. array := strings.Split(title, "-")
  1848. title = array[0]
  1849. title = strings.TrimSpace(title)
  1850. cookie, err := getCookies(wd)
  1851. if err != nil {
  1852. return err
  1853. }
  1854. fmt.Printf("***************正在处理%s课程%s\n", username, title)
  1855. specialCourse := isSpecialCourse(title)
  1856. fmt.Printf("is special:%v\n", specialCourse)
  1857. questionM, _ := LoadXlsx(fmt.Sprintf("%s.xlsx", title))
  1858. // 获取课程id
  1859. time.Sleep(2 * time.Second)
  1860. src, _ := wd.CurrentURL()
  1861. courseId := getCourseId(src)
  1862. if courseId == "" {
  1863. return errors.New("课程id获取失败")
  1864. }
  1865. fmt.Printf("课程id:%s\n", courseId)
  1866. // 获取课程所有栏目
  1867. modules, err := getCourseModules(courseId, cookie, specialCourse)
  1868. if err != nil {
  1869. return errors.New(fmt.Sprintf("获取module信息失败:%s", err.Error()))
  1870. }
  1871. fmt.Printf("modules:%v\n", modules)
  1872. examCount := 0
  1873. for _, module := range modules {
  1874. fmt.Printf("***课程:%s 栏目:%s\n", title, module.Name)
  1875. /*if specialCourse && (config.Conf.Target == 1 || config.Conf.Target == 3) && (!strings.Contains(module.Name, "形考任务") &&
  1876. !strings.Contains(module.Name, "形成性考核") &&
  1877. !strings.Contains(module.Name, "形考作业") &&
  1878. !strings.Contains(module.Name, "课程考核") &&
  1879. !strings.Contains(module.Name, "大作业") &&
  1880. !strings.Contains(module.Name, "考试考核")) {
  1881. fmt.Println("module name:", module.Name)
  1882. continue
  1883. }*/
  1884. if strings.Contains(module.Name, "专题一") ||
  1885. strings.Contains(module.Name, "专题二") ||
  1886. strings.Contains(module.Name, "专题三") ||
  1887. strings.Contains(module.Name, "专题四") ||
  1888. strings.Contains(module.Name, "专题五") ||
  1889. strings.Contains(module.Name, "总论 ") {
  1890. //continue
  1891. }
  1892. if false {
  1893. if !strings.Contains(module.Name, "专题三") {
  1894. continue
  1895. }
  1896. }
  1897. // 获取某栏目下的试题和视频数据
  1898. //fmt.Println("111111111111111111:", courseId, module.ModuleId)
  1899. moduleInfo, err := GetModuleInfo(courseId, module.ModuleId, cookie)
  1900. if err != nil {
  1901. return errors.New(fmt.Sprintf("获取module(%s)详细信息失败:%s", module.Name, err.Error()))
  1902. }
  1903. //fmt.Println("2222222222222:", moduleInfo)
  1904. // 处理网页数据
  1905. if false {
  1906. for _, info := range moduleInfo.TxtInfos {
  1907. fmt.Printf("*****正在处理网页数据:%s\n", info.Name)
  1908. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  1909. wd.Get(url)
  1910. count := 0
  1911. for count < 10 {
  1912. curl, _ := wd.CurrentURL()
  1913. if strings.Contains(curl, info.Id) {
  1914. time.Sleep(5 * time.Second)
  1915. break
  1916. }
  1917. count++
  1918. time.Sleep(1 * time.Second)
  1919. }
  1920. }
  1921. }
  1922. if (config.Conf.Target == 1 || config.Conf.Target == 2) && !strings.Contains(title, "国家开放大学学习指南") {
  1923. // 处理视频数据
  1924. for _, info := range moduleInfo.VedioInfos {
  1925. fmt.Printf("*****正在处理视频数据:%s\n", info.Name)
  1926. if info.Stype == "material" {
  1927. doMaterial(info.Id, cookie)
  1928. continue
  1929. }
  1930. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  1931. wd.Get(url)
  1932. cookie, _ := getCookies(wd)
  1933. // 特殊视频
  1934. if info.Stype == "web_link" {
  1935. array := strings.Split(info.Name, ":")
  1936. if len(array) > 1 {
  1937. array[0] = array[1]
  1938. }
  1939. str := fmt.Sprintf("//iframe[contains(@ng-src, '%s')]", array[0])
  1940. wd.WaitWithTimeout(Displayed(selenium.ByXPATH, str), time.Duration(config.Conf.Timeout)*time.Second)
  1941. continue
  1942. }
  1943. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//button[@class='mvp-toggle-play mvp-first-btn-margin']"), time.Duration(config.Conf.Timeout)*time.Second)
  1944. if err != nil {
  1945. wd.Get(url)
  1946. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//button[@class='mvp-toggle-play mvp-first-btn-margin']"), time.Duration(config.Conf.Timeout)*time.Second)
  1947. if err != nil {
  1948. fmt.Printf("获取播放按钮失败\n")
  1949. }
  1950. continue
  1951. //return err
  1952. }
  1953. playButton, _ := wd.FindElement(selenium.ByXPATH, "//button[@class='mvp-toggle-play mvp-first-btn-margin']")
  1954. er := playButton.Click()
  1955. //er := tryClick(wd, playButton)
  1956. if er != nil {
  1957. fmt.Printf("播放点击失败:%v\n", er)
  1958. return er
  1959. }
  1960. //fmt.Printf("cookie:%s\n", cookie)
  1961. time.Sleep(time.Duration(config.Conf.VideoInterval) * time.Second)
  1962. //er = tryClick(wd, playButton)
  1963. //if er != nil {
  1964. // fmt.Printf("停止播放点击失败:%v\n", er)
  1965. //}
  1966. //wd.Get(fmt.Sprintf("https://lms.ouchn.cn/course/%s/ng#/my-stat", courseId))
  1967. //time.Sleep(2*time.Second)
  1968. err := VedioCompleteHandle(cookie, info.Id)
  1969. if err != nil {
  1970. fmt.Printf(" 视频%s处理失败(%s)\n", info.Name, err.Error())
  1971. return err
  1972. }
  1973. //time.Sleep(10000*time.Second)
  1974. }
  1975. }
  1976. if config.Conf.Target == 1 || config.Conf.Target == 3 {
  1977. // 处理试题数据
  1978. for _, info := range moduleInfo.ExamInfos {
  1979. fmt.Printf("*****正在处理试题数据:%s\n", info.Name)
  1980. if len(questionM) == 0 && !config.Conf.QuestionRandom {
  1981. fmt.Printf("无法处理试题,课程%s未提供答案\n", title)
  1982. break
  1983. }
  1984. //if !strings.Contains(info.Name, "考试任务二") {
  1985. // continue
  1986. //}
  1987. examCount++
  1988. fmt.Printf("count:%d\n", examCount)
  1989. // 测试用的
  1990. if examCount < 0 {
  1991. continue
  1992. }
  1993. err := examHandle(wd, courseId, module.ModuleId, info.Id, questionM, specialCourse)
  1994. if err != nil {
  1995. fmt.Printf(" 试题%s处理失败(%s)\n", info.Name, err.Error())
  1996. //time.Sleep(1000 * time.Second)
  1997. if strings.Contains(err.Error(), "选项为空") ||
  1998. strings.Contains(err.Error(), "答案为空") {
  1999. continue
  2000. }
  2001. return err
  2002. }
  2003. time.Sleep(1 * time.Second)
  2004. //time.Sleep(1000*time.Second)
  2005. }
  2006. //time.Sleep(1000*time.Second)
  2007. }
  2008. // 再次处理网页数据
  2009. if true {
  2010. for i := 0; i < config.Conf.VisitCount; i++ {
  2011. for _, info := range moduleInfo.TxtInfos {
  2012. fmt.Printf("*****正在点击网页数据:%s\n", info.Name)
  2013. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  2014. wd.Get(url)
  2015. count := 0
  2016. for count < 10 {
  2017. curl, _ := wd.CurrentURL()
  2018. if strings.Contains(curl, info.Id) {
  2019. time.Sleep(2 * time.Second)
  2020. break
  2021. }
  2022. count++
  2023. time.Sleep(1 * time.Second)
  2024. }
  2025. }
  2026. }
  2027. }
  2028. }
  2029. if true {
  2030. //time.Sleep(1000*time.Second)
  2031. }
  2032. return nil
  2033. }