校园招新之获取进QQ群但未报名人员

news2024/9/21 12:47:16

校园的社团、实验室招新一般由是校领导会发一个QQ通知,让各个班的同学们进一个招新群。 群里面会有负责人提示大家报名,但是群成员不总是都会报名,我们需要的就是,找到那些,已经进群,但是没有报名的同学,然后私聊提醒一下。

大体思路:获取QQ群成员列表,获取已经报名的人员(从问卷星导出,使用go读取execl),根据两者(我们使用QQ群昵称 “计科203张三” 和 execl中的班级+姓名作为判断标准)即可求出我们想要的差集合并集。  

获取QQ群成员列表

参考文章:js解密之QQ的bkn值,获取QQ群成员信息,获取QQ好友列表信息-腾讯云开发者社区-腾讯云

 进入网站:https://qun.qq.com/member.html 登录,选择群,打开网络请求

解析参数:

  • gc,应该是群id
  • st: 开始下标 
  • end :结束下标
  • sort:不知道
  • bkn:加密用的应该是

解析response

  •  adm_num :管理员数量,不带群主
  • count: 群成员总数
  • mems : 群成员列表
  • mems.role : 0是群主,1是管理员,2是群成员
  • card: 群昵称

nick:QQ昵称

 使用网络抓包即可,导入apifox即可

 具体代码

package main

import (
	"encoding/json"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

type QQUser struct {
	Uin           int `json:"uin"`
	Role          int `json:"role"`
	G             int `json:"g"`
	JoinTime      int `json:"join_time"`
	LastSpeakTime int `json:"last_speak_time"`
	Lv            struct {
		Point int `json:"point"`
		Level int `json:"level"`
	} `json:"lv"`
	Card string `json:"card"`
	Tags string `json:"tags"`
	Flag int    `json:"flag"`
	Nick string `json:"nick"`
	Qage int    `json:"qage"`
	Rm   int    `json:"rm"`
}
type JSONData struct {
	Ec          int         `json:"ec"`
	Errcode     int         `json:"errcode"`
	Em          string      `json:"em"`
	Cache       int         `json:"cache"`
	AdmNum      int         `json:"adm_num"`
	Levelname   interface{} `json:"levelname"`
	Mems        []QQUser    `json:"mems"`
	Count       int         `json:"count"`
	SvrTime     int         `json:"svr_time"`
	MaxCount    int         `json:"max_count"`
	SearchCount int         `json:"search_count"`
	Extmode     int         `json:"extmode"`
}
type WJXUser struct {
	Class string
	Name  string
}

func ReadExcel(filename string) (RegisteredUserList []WJXUser, err error) {
	f, err := excelize.OpenFile(filename)
	if err != nil {
		return
	}
	sheets := f.GetSheetMap() //获取Execl表的工作表
	fmt.Println(sheets)
	sheet1 := sheets[1] //sheets是一个map,map[1]就是获取到第一个工作表
	fmt.Println("第一个工作表", sheet1)
	rows := f.GetRows(sheet1) //获取工作表的所有数据,数据存储在一个二维数组中,二维数组中的每一个一位数组就是一行数据
	fmt.Println(rows)
	RegisteredUserList = make([]WJXUser, 0)
	for i := 1; i < len(rows); i++ {
		name := strings.ReplaceAll(rows[i][6], " ", "")
		name = strings.ReplaceAll(name, "&nbsp;", "")
		class := strings.ReplaceAll(rows[i][9], " ", "")
		class = strings.ReplaceAll(class, "&nbsp;", "")
		class = strings.ReplaceAll(class, "班", "")
		RegisteredUser := WJXUser{Name: name, Class: class}
		RegisteredUserList = append(RegisteredUserList, RegisteredUser)
	}
	return RegisteredUserList, err
}
func main() {
	url := "https://qun.qq.com/cgi-bin/qun_mgr/search_group_members"
	method := "POST"
	client := &http.Client{}
	NotedUserList := make([]QQUser, 0)  // 已经进QQ群且已经备注人员 (已经剔除管理员)
	NoNoteUserList := make([]QQUser, 0) // 已经进QQ群但是未备注人员(已经剔除管理员)
	countMember := 0                    // 群成员总数量
	cycleIndex := 1                     // 刚开始让循环一轮,后面根据群成员总数来确定到底循环多少轮
	for i := 0; i < cycleIndex; i++ {
		st := i * 21
		end := st + 20
		if i != 0 && i == cycleIndex-1 { // For the fifth iteration, set the end to 86
			end = countMember
		}
		payload := strings.NewReader(fmt.Sprintf("gc=185335516&st=%d&end=%d&sort=0&bkn=336337277", st, end))
		req, err := http.NewRequest(method, url, payload)
		if err != nil {
			return
		}
		req.Header.Add("Accept", "application/json, text/javascript, */*; q=0.01")
		req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
		req.Header.Add("Connection", "keep-alive")
		req.Header.Add("Origin", "https://qun.qq.com")
		req.Header.Add("Referer", "https://qun.qq.com/member.html")
		req.Header.Add("Sec-Fetch-Dest", "empty")
		req.Header.Add("Sec-Fetch-Mode", "cors")
		req.Header.Add("Sec-Fetch-Site", "same-origin")
		req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
		req.Header.Add("X-Requested-With", "XMLHttpRequest")
		req.Header.Add("sec-ch-ua", "\"Google Chrome\";v=\"125\", \"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"")
		req.Header.Add("sec-ch-ua-mobile", "?0")
		req.Header.Add("sec-ch-ua-platform", "\"macOS\"")
		req.Header.Add("Cookie", "RK=Gdv1uMjH8g; ptcz=cc0188ccc671556dd56ea8ffb4ae59d282714e8e788826033f41c89d77b61b53; pgv_pvid=4845315920; tgw_l7_route=9d1d4698c4322116c7c255687ec1fe38; traceid=cfd4c1d7fb; uin=o3063360183; skey=@61HarNEbA; p_uin=o3063360183; pt4_token=e6WELkURyGZz0yu3RI3j1UwIQk5z9E7n9dUrbXmq4gE_; p_skey=GQ0pK0oDZsKCwOphrmWqZtFoCE5qsUVEqZfgDLHn7FU_")
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
		req.Header.Add("Host", "qun.qq.com")
		res, err := client.Do(req)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer res.Body.Close()

		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			fmt.Println(err)
			return
		}

		var r JSONData
		err = json.Unmarshal(body, &r)
		countMember = r.Count
		cycleIndex = countMember/21 + 1 // 每次查询是21人,不足21次的向上取整一下
		if err != nil {
			fmt.Println("Error unmarshalling response:", err)
			return
		}
		for j := 0; j < len(r.Mems); j++ {
			if r.Mems[j].Role == 2 {
				if r.Mems[j].Card == "" {
					NoNoteUserList = append(NoNoteUserList, r.Mems[j])
				} else {
					// 数据处理,让数据保持格式为 “计科221张三”
					r.Mems[j].Card = strings.ReplaceAll(r.Mems[j].Card, " ", "")
					r.Mems[j].Card = strings.ReplaceAll(r.Mems[j].Card, "&nbsp;", "")
					r.Mems[j].Card = strings.ReplaceAll(r.Mems[j].Card, "班", "")
					NotedUserList = append(NotedUserList, r.Mems[j])
				}

			}
		}
		time.Sleep(2 * time.Second)
	}
	registeredUserList, err := ReadExcel("/Users/yjppjy/Downloads/263778738_按序号_π-Team报名表_54_51.xlsx") // 已经报名人员
	if err != nil {
		return
	}
	// 根据 NoteUserList中的.Card字段 和 registeredUserList的 Class + Name 作为判断依据,以QQUser为基准,找出来 未进群但是已报名、已进群已报名、已进群未报名的人员
	// 将已报名人员存储在一个map中,以便快速查找
	registeredUserMap := make(map[string]WJXUser)
	for _, user := range registeredUserList {
		key := user.Class + user.Name
		registeredUserMap[key] = user
	}

	// 查找已进群已报名、已进群未报名
	var notedAndRegistered []QQUser
	var notedAndNotRegistered []QQUser

	for _, qqUser := range NotedUserList {
		key := qqUser.Card
		if _, found := registeredUserMap[key]; found {
			notedAndRegistered = append(notedAndRegistered, qqUser)
		} else {
			notedAndNotRegistered = append(notedAndNotRegistered, qqUser)
		}
	}

	// 查找未进群但已报名
	var notInGroupButRegistered []WJXUser
	for key, wjxUser := range registeredUserMap {
		found := false
		for _, qqUser := range NotedUserList {
			if qqUser.Card == key {
				found = true
				break
			}
		}
		if !found {
			notInGroupButRegistered = append(notInGroupButRegistered, wjxUser)
		}
	}

	// 输出结果
	//fmt.Println("已进群已报名:")
	//for _, user := range notedAndRegistered {
	//	fmt.Println(user.Card, user.Nick)
	//}

	fmt.Println("已进群未报名:")
	for _, user := range notedAndNotRegistered {
		fmt.Println(user.Card, user.Nick)
	}

	fmt.Println("未进群但已报名:")
	for _, user := range notInGroupButRegistered {
		fmt.Println(user.Class, user.Name)
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1696825.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SketchUp v2024 v24.0.553 解锁版安装教程 (强大的绘图三维建模工具)

前言 SketchUp&#xff08;简称SU&#xff0c;俗称草图大师&#xff09;全球知名的三维建模软件&#xff0c;强大的绘图工具、建模渲染、扩展插件和渲染器模板、海量3D模型库及建模灯光材质渲染效果图&#xff0c;用于建筑师、城市规划专家、游戏开发等行业。 一、下载地址 …

一行代码实现UI拖拽的效果

演示 先来看效果吧&#xff01; 实现方式 1.首先创建一个你想拖动的UI图片 2.创建一个C#的脚本 3.编写控制脚本&#xff08;代码按我的敲就行&#xff09; 付上代码片段 public void OnDrag(PointerEventData eventData){transform.position eventData.position;} 4.添加脚…

linux系统——top资源管理器

在linux系统中&#xff0c;有类似于windows系统中的资源管理器&#xff0c;top用于实时的监控系统的任务执行状态以及硬件配置信息 在linux中&#xff0c;输入top命令&#xff0c;可以进入相应界面&#xff0c;在此界面可以使用一些指令进行操作 如&#xff0c;输入z 可以改变…

github加速访问及资源一秒代理下载

如果你想加速打开github网页&#xff0c;可以采用以下方法&#xff0c;仅需一个插件。 1.代理加速访问 打开gitee网站&#xff0c;搜索dev-sidecar关键字&#xff0c;然后找到星星最多的项目 可以阅读项目说明&#xff0c;找到感兴趣的内容或是直接下载DevSidecar桌面应用程序…

stm32学习-流水灯

接线 注意&#xff1a;LED灯长一点的引脚是正极。 配置GPIO 1.使用RCC开启GPIO时钟 void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); void RCC_APB1Perip…

电子电器架构 - 车载网管功能简介

电子电器架构 - 车载网管功能简介 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,…

OZONBIGSELL分析产品工具,OZON平台数据分析

在当今的数字化时代&#xff0c;电商平台的竞争日趋激烈&#xff0c;数据成为了企业决策的重要支撑。OZON作为俄罗斯领先的电商平台&#xff0c;其背后蕴含的海量数据对于卖家而言&#xff0c;既是挑战也是机遇。接下来看看OZONBIGSELL这一分析产品工具&#xff0c;以及如何利用…

【实战教程】使用Spring AOP和自定义注解监控接口调用

一、背景 随着项目的长期运行和迭代&#xff0c;积累的功能日益繁多&#xff0c;但并非所有功能都能得到用户的频繁使用或实际上根本无人问津。 为了提高系统性能和代码质量&#xff0c;我们往往需要对那些不常用的功能进行下线处理。 那么&#xff0c;该下线哪些功能呢&…

C从零开始实现贪吃蛇大作战

个人主页&#xff1a;星纭-CSDN博客 系列文章专栏 : C语言 踏上取经路&#xff0c;比抵达灵山更重要&#xff01;一起努力一起进步&#xff01; 有关Win32API的知识点在上一篇文章&#xff1a; 目录 一.地图 1.控制台基本介绍 2.宽字符 1.本地化 2.类项 3.setlocale函…

原生标签WebComponent

文章目录 介绍一、web Component二、怎么使用三、在Vue中使用使用场景 前端必备工具推荐网站(免费图床、API和ChatAI等实用工具): http://luckycola.com.cn/ 介绍 平常浏览各个网站过程中&#xff0c;经常遇到的一种现象&#xff1a;页面广告。 这种广告按照来源可分为两种&…

Python TinyDB库:轻量级NoSQL数据库的终极指南

更多Python学习内容&#xff1a;ipengtao.com TinyDB是一个轻量级的NoSQL数据库&#xff0c;适用于需要嵌入式数据库的小型项目。它使用JSON文件存储数据&#xff0c;并提供了简单易用的API&#xff0c;支持多种查询和索引操作。TinyDB非常适合那些不需要复杂数据库功能的小型应…

C++Qt操作Lotus Domino数据库 Lotus Domino C++连接Lotus Domino C++快速开发Lotus Domino

java连接domino C#连接domino python连接domino go连接domino,delphi连接domino Excel连接domino Flutter、微信小程序连接domino C 操作 Lotus Domino 数据库&#xff1a;自动化与效率的结合 引言 在企业级应用中&#xff0c;Lotus Domino 提供了一个强大的协作平台&#xff0…

【吊打面试官系列】Java高并发篇 - ThreadLocal 是什么?有什么用?

大家好&#xff0c;我是锋哥。今天分享关于 【ThreadLocal 是什么&#xff1f;有什么用&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; ThreadLocal 是什么&#xff1f;有什么用&#xff1f; ThreadLocal 是一个本地线程副本变量工具类。主要用于将私有线程和该…

2022年CSP-J入门级第一轮初赛真题

一、单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1b;每题有且仅有一个正确选项&#xff09; 第 1 题 在内存储器中每个存储单元都被赋予一个唯一的序号&#xff0c;称为&#xff08;&#xff09;。 A. 地址B. 序号C. 下标D. 编号 第 2 题 编…

电脑怎么录屏?电脑录屏的7个方法,仅3%的人知道!

你知道电脑怎么录屏吗&#xff1f;在电脑上录屏是向朋友展示炫酷游戏技巧、制作软件教程视频和展示数字艺术技巧的好方法。遗憾的是&#xff0c;屏幕录制并不像截屏那么简单。然而&#xff0c;无论你是在寻找在电脑上录制屏幕&#xff0c;亦或是录制音频的方法&#xff0c;还是…

C/C++|malloc分配内存详解

看本节前&#xff0c;希望读者有linux内存分布的基本概念&#xff0c;可以阅读这篇文章&#xff1a; 进程虚拟地址空间和函数调用栈 在本节中希望读者可以一口气阅读完所有内容。 本博客内容全部来自小林coding&#xff1a;malloc 是如何分配内存的&#xff1f; 这里仅为笔记记…

基于docxtpl的模板生成Word

docxtpl是一个用于生成Microsoft Word文档的模板引擎库。它结合了docx模块和Jinja2模板引擎&#xff0c;使用户能够使用Microsoft Word模板文件并在其中填充动态数据。这个库提供了一种方便的方式来生成个性化的Word文档&#xff0c;并支持条件语句、循环语句和变量等控制结构&…

Mycat+Mysql搭建数据集群实现数据分片存储

前言 MyCAT介绍 * 一个彻底开源的,面向企业应用开发的“大数据库集群”; * 支持事务、ACID、可以替代MySQL的加强版数据库; * 一个可以视为“MySQL”集群的企业级数据库,用来替代昂贵的Oracle集群; * 一个融合内存缓存技术、Nosql技术、HDFS大数据的新型SQL; * 一个新颖…

天诚公租房/人才公寓WiFi人脸识别物联网智能门锁解决方案

人才是引领城市高质量发展的重要因素&#xff0c;城市要想吸纳人才的保障便是人才公寓。近年来&#xff0c;全国各地一二三线城市都在大力建设人才公寓&#xff0c;集聚菁英人才&#xff0c;倾力打造人才高地。 一、人才公寓如火如荼建设 2023年底&#xff0c;山东德州提出三年…