记一次被 AirTag 跟踪的经历

来自: https://blog.meathill.com/device/an-experience-of-being-followed-by-airtag.html

前些天,我们几家人一起去川西自驾游,旅行到第 5 天的时候,我突然收到一条推送:



发现正在跟随您移动的 AirTag

此物品的物主可查看其位置。轻点打开“查找”并查看可用操作。

我是 TestV 的铁丝,对 AirTag 闻名已久,于是这条推送立刻引起了我的注意。我想了想,像我这种家里蹲,应该没人想追踪我。同游的亲戚里,倒是有社会关系比较复杂的;而且话说回来,如今这个年代,家里人互相丢个 AirTag 也不是不可能。

于是我先找到老婆,本意是让她先找大家试试口风,没想到她立刻就跟所有人都说了,然后全家总动员,开始寻找陌生人放下的 AirTag。

0. 关于 AirTag

其实 AirTag 是个不小的发明。它本身不支持联网,无法使用 Wi-Fi 或者 4G/5G;不支持存储;也不支持定制发送的内容。这些“瘦身手段”使得它的构造可以尽量简单,体积小巧;尽量省电,一颗普通纽扣电池可以使用很长时间。

AirTag 会定时向周围广播位置,只有苹果设备认得这些加密数据,它们收到广播后,如果处于联网状态,就会把这些信息转发给苹果的中心服务器。AirTag 的拥有者可以登录到苹果服务器查看 AirTag 的位置。

AirTag 可以用来追踪物品,但是也可以拿来追踪人。

1. 为什么我会收到推送

正常情况下,这些数据会通过拥有者的设备转发(因为他们会和 AirTag 长期近距离接触);或者在开放区域,就会被一批随机设备转发。

我们的情况比较特殊:我们在川西自驾,很长一段路程甚至连网络都没有,于是 AirTag 周围就只有屈指可数的几台苹果设备可以作为转发渠道。而这几台设备都不是 AirTag 拥有者的设备,当一个设备反复帮不是同一个 Apple ID 的 AirTag 转发位置信息后,就会触发苹果的风控系统,苹果就要通知 ta:

小心,可能有人在跟踪你。

2. 寻找 AirTag

按照推送,打开”查找“,可以看到这个 AirTag 一路跟着我们跨域几百公里,从昨晚的酒店来到今天的酒店,甚至包含中间去做核酸的当地医院。基本可以排除偶遇同路,一定是有人故意放在车上、或者行李里。

”查找“可以让 AirTag 发出声音,于是我在几个房间转了一圈,确认无法连接。猜测 AirTag 应该在车上,于是我带着一群小朋友(其实也不小了)来到停车场,能连接的上,但是没有声音。那么只有两个可能:

  1. 喇叭被拆掉了
  2. 藏的比较深,声音传不出来

为排除(2),大家开始翻行李。翻来翻去,翻去翻来,始终找不到,多半是喇叭被拆掉了。看来放 AirTag 的人处心积虑不想让人找到。眼看越来越晚,我就把大家拉回去了,今晚先放弃。

我回去搜了半天,但没找到什么有价值的信息。能够帮被追踪者查找 AirTag 的方式,只有播放声音,或者蓝牙信号定位。前者拆了喇叭就白给,后者则要求环境可控,比如能够关闭或屏蔽大部分蓝牙设备,但现实中很难做到。如果是自己的 AirTag,苹果提供了非常丰富的定位手段;而别人的 AirTag,就没法使用。

第二天,我尝试了前一晚积累的几个想法,都没成功。于是大家决定先放弃。大家都认为不会是追踪自己,多半跟原车主有关,为了接下来的旅程,就先不管吧。

3. 答案

回家之后,经询问,AirTag 是其中一位车主放在车上的,为的是好找车。

事实就是这么简单,跟吴啊萍一样。

4. 想法

这件事情之后,我对 AirTag 的不信任进一步加深了。

  1. AirTag 很小、很薄、很容易藏。攻防两方的地位不对等,被投放的人想找到 AirTag 很困难。
  2. AirTag 并不如设计的那般可靠,拆喇叭很容易,拆掉之后几乎没法寻找。
  3. AirTag 非常容易买到,没有持有成本。
  4. AirTag 的认知度非常低,大外甥告诉我,他两天前就接到过推送,但他根本不了解 AirTag,没多想就直接关掉了。

如果有个人存心想追踪另外一个人,他可以用很低的成本(rmb 100+)了解到对方的行踪,对方可能长时间都不知道自己处于被跟踪状态。苹果应该设计更好的方案,或者增加 AirTag 的持有和使用成本。

 

创作没了,心凉

来自: https://www.v2ex.com/t/867078

 

突然被封

7 月 15 号(上周五)下午 17:03 ,我在出差回京的高铁上。突然接到腾讯云邮件、站内信通知,我的在线思维导图、流程图工具——画图本 huatuben.com 域名因涉嫌欺诈,被停止解析,同时禁止解封、禁止域名转移。

一瞬间,整个人都不好了。

最开始的挣扎

高铁上信号差,七拐八拐终于联系上了腾讯云客服。被告知执行监管部门指令,不得解封、不得转移。同时,不告知是哪个部门封禁的、也不告知封禁的具体原因,自称没有权限。

周六日两天,心如死灰。

不死心

今天周一,各部门上班了。不死心,致电工信部备案中心,对方反馈没有封禁操作。邮件给反诈中心,对方告知 5/31 日解封后也没有任何操作。

继续找腾讯云客服,依然不告知原因、不告知受什么监管部门的指令。在我连续追问下,被逼急了,一直跟我回复法条,并开启复读机模式,无论我问什么问题,都只发送法条。

要求腾讯云技术专员电话联系我。几小时来电,依然复读机,自称无法告知谁封禁的、无法告知原因。

决定放弃

到现在,基本确定是个死局。估计即使报警、拨打 12315 热线,也解决不了问题。神秘力量太强大了。无奈,只能放弃。域名肯定要不回来了,好在网站数据还在,因此打算新注册个域名,把服务整体迁移到到海外。

尾巴

这个网站倾注了我太多心思和精力,奔四的人了,和小伙伴配合弄个小工具站,想着多少能有个自己的事情做,算是个中年人的念想。把域名从国外转到国内,服务器也迁到腾讯云。工信部备案,公安备案,一顿操作折腾。从春节到现在,每个假期、每天下班,基本没有休息一直在改进使用体验。这一路都走过来了,也积累了一批原始用户。

这一下瞬间给关了,愤怒,心里凉的透透的。发个贴,舒缓一下。希望能给其他独立开发者作个参考,别再掉我这个坑。

顺便问一句,想给国内用户提供在线服务,有啥不被封的方法么?咱就做个学习工具而已,太难了!





如何用一个(Git) 仓库记录自己的一年

来自 https://github.com/yihong0618/gitblog/issues/209

写在前面

从最开始的用 GitHub 的 Issues 写博客,到尝试用 GitHub 记录的年度数据已经二年有余了,从最开始有想法 2020, 到 2021 加了很多功能,把大部分的记录自动化,一点一点记录也算是有些心得了,这篇文章就介绍一下我是怎么做的,能帮助到同样感兴趣的大家就更好了。也算是践行这 2 年对我影响最大的文章之一

People Die, but Long Live GitHub

GitHub 的一个 repo 能做什么?

  1. 能记录 Issues
  2. Issues 可以评论
  3. 能给 Issue 打标签
  4. 能有一张展示的 README
  5. README 能引入 svg
  6. 有 Actions 可以辅助自动化
  7. Actions 可以放 secrets
  8. 可以用 cron 和各种事件触发 Actions
  9. API 公开,可以自己 DIY
  10. 有手机应用,不用开电脑能随时随地记录
  11. 有评论区
  12. 私有仓库这些以上的都有

好,我们把这些结合起来,记录自己的一年~

数字区

image
上图这些数字区是怎么实现方式分为两部分:

  • API 获取 -- 扇贝,开心词场,多邻国
  • Issues 配合 labels 获取,俯卧撑,花费。。。

每天早 8 点和晚 9 点,定时跑一次,计算数据进行整合,并配合 telegram 的 bot 提醒自己。
入口大家可以参考 Actions 的这个 workflow

Issues 的代码思路为获取 label -> 通过 label 找 issues -> issues 的评论特定格式 -> 通过函数解析 -> 整合 -> 通过正则替换 README 中的原有数据(文字) (README 可以写注释,而注释是不显示的,利用这个完成显示和替换)

def main(
    login_dict,
    github_token,
    repo_name,
):
    my_num_stat_str = MY_NUMBER_STAT_HEAD
    # API STAT STR
    for name, value_dict in MY_STATUS_DICT_FROM_API.items():
        try:
            url = value_dict.get("url")
            md_name = f"[{name}]({url})"
            # maybe a better way?
            total_data, streak, today_check = value_dict.get("daily_func")(
                *login_dict.get(name, tuple())
            )
            total_data_str = str(total_data) + value_dict.get("unit_str", "")
            my_num_stat_str += make_stat_str(
                md_name, total_data_str, streak, today_check
            )
        # just a tricky code for others for use
        except Exception as e:
            print(e)
            continue

    u = Github(github_token)
    # COMMENTS STAT STR
    for name, value_dict in MY_STATUS_DICT_FROM_COMMENTS.items():
        try:
            labels, map_func, reduce_func = LABEL_DAILY_DICT.get(name)
        except:
            # tricky for mine
            continue
        func = value_dict.get("daily_func")
        if not func:
            break

        issues = u.get_repo(repo_name).get_issues(labels=labels)
        total_data, streak, today_check, url, month_summary_dict = func(
            issues, map_func, reduce_func
        )
        # change the issue body for month summary
        unit = value_dict.get("unit_str", "")
        for i in issues:
            body = ""
            for b in i.body.splitlines():
                # from the summary table
                if b.startswith("|"):
                    break
                body += b + "\r\n"
            body = body + "\r\n" + make_month_summary_str(month_summary_dict, unit)
            # edit this issue body
            i.edit(body=body)
        name = f"[{name}]({url})"
        total_data_str = str(total_data) + unit
        my_num_stat_str += make_stat_str(name, total_data_str, streak, today_check)

    replace_readme_comments("README.md", my_num_stat_str, "my_number")

多说几句早起

关于早起这个数据和 issue 因为自己喜欢诗歌,我找了一个获取一句诗的 API, 然后自动评论,评论是带时间戳的,正好记录自己的起床时间。
那么是怎么触发的呢?
我用的是 iOS 系统,而 iOS 有个重要的功能是“捷径“。利用捷径可以触发 Actions workflow 的 api, 触发 api 就有时间戳了,再判断是不是早起,给自己发送就好了。
捷径的触发条件是闹钟关闭,为了保险起见,我可能比闹钟起的早,再加一条关闭背单词软件,解决了用 GitHub 记录早起的问题。

def make_get_up_message():
    sentence = get_one_sentence()
    now = pendulum.now(TIMEZONE)
    # 3 - 6 means early for me
    is_get_up_early = 3 <= now.hour <= 6
    get_up_time = now.to_datetime_string()
    body = GET_UP_MESSAGE_TEMPLATE.format(get_up_time=get_up_time, sentence=sentence)
    return body, is_get_up_early

关于如何利用捷径配合 Actions 我写过一篇文章 -- 巧妙利用 iOS 的快捷指令配合 GitHub Actions 实现自动化
image
而早起这句诗,我期待好久的 -- 苟利国家生死以,岂因福祸避趋之。还没随机到,随到这一天我决定跑 19.26 km.

GitHubPost 区

利用了我写的 GitHubPoster 项目,自动生成 svg 引入,而自动跑的脚本也在那个项目上。
image

GitHub Repos 区

利用我写的 github-readme-stats 自动替换生成。
image
image

娱乐区

全部利用 Issues 评论 -> 触发 Actions -> 自动替换 README 的注释区域生成
image

def replace_readme_comments(file_name, comment_str, comments_name):
    with open(file_name, "r+") as f:
        text = f.read()
        # regrex sub from github readme comments
        text = re.sub(
            GITHUB_README_COMMENTS.format(name=comments_name),
            r"\1{}\n\3".format(comment_str),
            text,
            flags=re.DOTALL,
        )
        f.seek(0)
        f.write(text)
        f.truncate()

做饭区

和上面类似,因为做的太多,我生成了个表格
image

def parse_cook_issue_table(me, issues):
    comments_str = MY_FOOD_STAT_HEAD
    food_dict = defaultdict(lambda: ["", "", 0])
    for issue in issues:
        comments = issue.get_comments()
        for c in comments:
            if not isMe(c, me):
                continue
            date_str = format_time(c.created_at)
            food_list_str = c.body.splitlines()[0]
            food_list = food_list_str.split(" ")
            for food in food_list:
                if food not in food_dict:
                    food_dict[food][0] = f"[{date_str}]({c.html_url})"
                    food_dict[food][1] = f"[{date_str}]({c.html_url})"
                else:
                    food_dict[food][1] = f"[{date_str}]({c.html_url})"
                food_dict[food][2] += 1
    for k, v in food_dict.items():
        comments_str += MY_FOOD_STAT_TEMPLATE.format(
            name=k, first_date=v[0], last_date=v[1], times=v[2]
        )
    return comments_str

月度数据

每次自动生成还会 edit issue 的内容生成月度数据整合
image

博客区

在 blog 的 repo 写 issue ->curl 2021 的 Actions workflow api -> 自动生成
image
image

收藏文章博客区

这个完全手动添加,我想保留一些仪式感
image

我也想做一个

如果大家想同样做一个这样的年度数据可以 follow 以下步骤:

  1. fork or clone 这个项目
  2. 增加 issues 打 label
  3. 修改 config
  4. 修改一些 api 的 config 数据换成自己的
  5. 如果有问题可以邮件或私信我
COOK_LABEL_LIST = [
    "Cook",
]
MOVIE_LABEL_LIST = [
    "Movie",
]
READ_LABEL_LIST = [
    "Read",
]
DRAMA_LABEL_LIST = [
    "Drama",
]
PUSHUP_LABEL_LIST = [
    "PushUps",
]
BANGUMI_LABEL_LIST = [
    "Bangumi",
]
GAME_LABEL_LIST = [
    "Game",
]
MONEY_LABEL_LIST = [
    "Money",
]
MEDITATION_LABEL_LIST = [
    "Meditation",
]
MORNING_LABEL_LIST = [
    "Morning",
]
GTD_LABEL_LIST = [
    "GTD",
]
MY_BLOG_REPO = "yihong0618/gitblog"
GITHUB_README_COMMENTS = (
    "(<!--START_SECTION:{name}-->\n)(.*)(<!--END_SECTION:{name}-->\n)"
)

# add new label here
LABEL_DICT = {
    "Cook": {"label_list": COOK_LABEL_LIST, "comment_name": "my_cook"},
    "Movie": {"label_list": MOVIE_LABEL_LIST, "comment_name": "my_movie"},
    "Read": {"label_list": READ_LABEL_LIST, "comment_name": "my_read"},
    "Drama": {"label_list": DRAMA_LABEL_LIST, "comment_name": "my_drama"},
    "Bangumi": {"label_list": BANGUMI_LABEL_LIST, "comment_name": "my_bangumi"},
    "Game": {"label_list": GAME_LABEL_LIST, "comment_name": "my_game"},
}


##### SHANBAY ######
MY_SHANBAY_USER_NAME = "ufewz"
SHANBAY_CALENDAR_API = "https://apiv3.shanbay.com/uc/checkin/calendar/dates/?user_id={user_name}&start_date={start_date}&end_date={end_date}"
MY_SHANBAY_URL = f"https://web.shanbay.com/web/users/{MY_SHANBAY_USER_NAME}/zone"

##### DUO ######
MY_DUOLINGO_URL = "https://www.duolingo.com/profile/yihong0618"

##### CICHANG ######
MY_CICHANG_URL = "https://twitter.com/yihong06181/status/1359040099107897344?s=20"


##### FOOD ######
MY_FOOD_STAT_HEAD = (
    "| Name | First_date | Last_date | Times | \n | ---- | ---- | ---- | ---- |\n"
)
MY_FOOD_STAT_TEMPLATE = "| {name} | {first_date} | {last_date} | {times} |\n"

##### Month Summary ######
MONTH_SUMMARY_HEAD = "| Month | Number | \n | ---- | ---- | \n"

MONTH_SUMMARY_STAT_TEMPLATE = "| {month} | {number} |\n"