Skip to content

Instantly share code, notes, and snippets.

@Verssae
Last active April 8, 2026 05:21
Show Gist options
  • Select an option

  • Save Verssae/0ed510986e87a8360965daaef3608861 to your computer and use it in GitHub Desktop.

Select an option

Save Verssae/0ed510986e87a8360965daaef3608861 to your computer and use it in GitHub Desktop.
한양대 안전교육 스킵 스크립트
/*
Hanyang Univ. a script for skipping safetyedu courses
http://safetyedu.hanyang.ac.kr/
[교육 영상 스킵]
1. '수강하기' 버튼을 눌러 안전교육 창을 띄운다.
2. 개발자도구(F12 버튼을 눌러)를 열어 'console' 탭에 이동해 아래 스크립트를 붙여넣는다.
3. 6과목에 대해 반복한다.
ref: https://kysgh2.tistory.com/113
*/
var currentPage = nowPageNum;
function goNextPage() {
if (currentPage < totalPageNum) {
opener.PageMove(currentPage);
console.log(`${currentPage} 페이지를 수강완료했습니다.`);
currentPage += 1;
setTimeout(function () {
goNextPage();
}, 100);
} else {
opener.PageMove(currentPage);
console.log('강의 수강이 완료되었습니다!');
setTimeout(function () {
window.close();
}, 200);
}
}
setTimeout(function () {
goNextPage();
}, 100)
/*
[퀴즈 스킵]
1. 6과목을 다 듣고 '평가하기' 버튼을 누른다.
2. 개발자도구(F12 버튼을 눌러)를 열어 'console' 탭에 이동해 아래 스크립트를 붙여넣는다.
*/
SetExamAfeter();
let answers = Array.from({length: questionCountInExam}, (x,i) => document.getElementById("qustionCorrectNo_" + i).value);
SetExamBefore();
for (let i=0; i<questionCountInExam; i++) {
let ls = document.getElementsByName(`qustionAnswerList[${i}].Answer`);
console.log(ls);
ls[answers[i]-1].checked = true;
}
document.getElementById("Exam_btnSave").click();
@shyuuuuni
Copy link
Copy Markdown

좋은 정보 감사합니다.

@qsc7342
Copy link
Copy Markdown

qsc7342 commented Nov 15, 2022

유익한 글이에요.

@0seob
Copy link
Copy Markdown

0seob commented Nov 16, 2022

최근에 본 글 중 가장 퀄리티가 높네요.

@milbae
Copy link
Copy Markdown

milbae commented Mar 27, 2023

유익한 글입니다 감사합니다!

@dev-jjjjjeong-bin
Copy link
Copy Markdown

좋은 정보 감사합니다 벌새님

@MichaelKimseongje
Copy link
Copy Markdown

감사합니다.

@hungrypro7
Copy link
Copy Markdown

감사합니다

@jang-geon
Copy link
Copy Markdown

비정상 방법이라고 초기화 되네요ㅜ

@Vari2ty331
Copy link
Copy Markdown

2024년 2학기 도중에 새 엔진이 들어와서 그런지 작동 안하는 강의가 있네요

@Vari2ty331
Copy link
Copy Markdown

2024년 2학기 도중에 새 엔진이 들어와서 그런지 작동 안하는 강의가 있네요

document.querySelector('video').playbackRate = 16;

동영상 전용이여서 그런가봅니다. 동영상에 최대 16배속 걸어두면 빠르게 해결됩니다.

@kcyoow
Copy link
Copy Markdown

kcyoow commented Dec 17, 2024

동영상 전용 해결방법
const formData = new FormData();
formData.append('scheduleMemberProgressNo', '1361183(예시)'); // 영상 링크중에 scheduleMemberProgressNo 뒤에 번호 넣으시면 됩니다
formData.append('currentTime', '1');
formData.append('isEnd', 'true');

fetch('https://safetyedu.hanyang.ac.kr/Edu/AviProcessCheck', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
console.log('서버 응답:', data);
})
.catch(error => {
console.error('에러 발생:', error);
});

"서버 응답: {"IsSuccess":true,"Msg":""}"
요렇게 뜨시면 나가시고 새로고침 하면 적용됩니다.
몇번 테스트해보긴 했는데 일단 저는 잘 작동했습니다.

@gepetton
Copy link
Copy Markdown

gepetton commented Mar 9, 2025

동영상 전용(2025 ver.) + 2학기 수정

const urlParams = new URLSearchParams(window.location.search);
const scheduleMemberProgressNo = urlParams.get('scheduleMemberProgressNo');

const formData = new FormData();
formData.append('scheduleMemberProgressNo', scheduleMemberProgressNo);
formData.append('currentTime', '1');
formData.append('isEnd', 'true');

fetch('https://safetyedu.hanyang.ac.kr/Edu/AviProcessCheck', {
    method: 'POST',
    mode: 'no-cors',
    body: formData
})
.then(response => {
    console.log('요청 전송 완료:', response);
    return response.text();
})
.then(data => {
    console.log('서버 응답:', data);
})
.catch(error => {
    console.error('에러 발생:', error);
});

스크립트 입력 후 팝업 닫고 새로고침하면 적용됩니다

@minkyungson
Copy link
Copy Markdown

감사합니다:)

@minkyungson
Copy link
Copy Markdown

동영상 전용(2026 ver.)

(function() {
// 1. 확인된 번호 입력
const myNo = '1632055';

const params = new URLSearchParams();
params.append('scheduleMemberProgressNo', myNo);
params.append('currentTime', '2500'); // 40분 분량에 맞춰 2500초로 설정
params.append('isEnd', 'true');       // 수강 완료를 위해 true로 변경
params.append('isMobile', 'false');   // 확인하신 필수값 추가

console.log(`[${myNo}] 번 과목 최종 정밀 요청 중...`);

fetch('/ushm/edu/contentsViewAviProcessCheckSub.do', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        'X-Requested-With': 'XMLHttpRequest'
    },
    body: params
})
.then(response => {
    console.log('HTTP 상태 코드:', response.status);
    return response.text();
})
.then(data => {
    console.log("서버 응답 결과:", data);
    // 응답에 "success"나 "true"가 포함되어 있는지 확인
    if (data.includes("true") || response.status === 200) {
        console.log("%c ✅ 수강 완료 처리에 성공한 것으로 보입니다!", "color: #2ecc71; font-weight: bold; font-size: 14px;");
        alert("처리가 완료되었습니다. 메인 페이지를 새로고침하세요!");
    }
})
.catch(error => {
    console.error("⚠️ 네트워크 에러:", error);
});

})();

스크립트 입력 후 팝업 닫고 새로고침하면 적용됩니다!

@Eric-Zhang537
Copy link
Copy Markdown

(async function simulateRealProgress() {
'use strict';

// ---------- 1. 自动收集页面关键参数 ----------
const paramGetters = [
    () => document.querySelector('input[name="scheduleMemberProgressNo"]')?.value,
    () => document.querySelector('input[name="scheduleNo"]')?.value,
    () => document.querySelector('input[name="courseNo"]')?.value,
    () => document.querySelector('input[name="contentNo"]')?.value,
    () => document.querySelector('input[name="memberNo"]')?.value,
    () => document.querySelector('meta[name="csrf-token"]')?.content,
    () => window.csrfToken,
    () => window._csrf,
    () => {
        const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
        return match ? match[1] : null;
    }
];

const baseParams = {};
paramGetters.forEach(getter => {
    try {
        const val = getter();
        if (val) {
            const name = getter.toString().match(/name="([^"]+)"/)?.[1] || 'csrfToken';
            baseParams[name] = val;
        }
    } catch (e) {}
});

// 硬编码的 scheduleMemberProgressNo(如果自动抓取失败则使用用户提供的)
const scheduleMemberProgressNo = baseParams.scheduleMemberProgressNo || '1632055';
console.log(' 基础参数:', baseParams);

// ---------- 2. 获取视频元素并计算总时长 ----------
const video = document.querySelector('video');
if (!video) {
    console.error('未找到 video 元素,无法继续');
    return;
}

// 确保视频元数据已加载
if (video.readyState < 1) {
    console.log(' 等待视频元数据加载...');
    await new Promise(resolve => {
        video.addEventListener('loadedmetadata', resolve, { once: true });
        video.load();
    });
}

const totalDuration = Math.ceil(video.duration);
console.log(`视频总时长: ${totalDuration} 秒 (${Math.floor(totalDuration/60)}分${totalDuration%60}秒)`);

// ---------- 3. 模拟播放行为(触发视频事件,绕过部分检测) ----------
console.log('模拟播放行为...');
video.currentTime = 0;
video.play().catch(() => {});  // 忽略自动播放限制错误

// 等待一小段时间,让浏览器记录“播放”动作
await new Promise(r => setTimeout(r, 1500));
video.pause();

// ---------- 4. 分步上报进度(模拟真实观看) ----------
const steps = 1;  // 上报次数,越多越真实
const stepTime = totalDuration / steps;

for (let i = 1; i <= steps; i++) {
    const currentTime = Math.floor(stepTime * i);
    const isEnd = (i === steps);  // 最后一次才标记完成
    
    video.currentTime = currentTime;

    video.dispatchEvent(new Event('timeupdate', { bubbles: true }));
    
    // 构造请求参数
    const formData = new URLSearchParams();
    formData.append('scheduleMemberProgressNo', scheduleMemberProgressNo);
    formData.append('currentTime', currentTime);
    formData.append('studyTime', currentTime);
    formData.append('progressRate', Math.floor((currentTime / totalDuration) * 100));
    formData.append('isEnd', isEnd ? 'true' : 'false');
    formData.append('isMobile', 'false');
    
    // 追加自动抓取的其他参数
    Object.entries(baseParams).forEach(([key, val]) => {
        if (!formData.has(key)) formData.append(key, val);
    });

    // 发送请求
    console.log(`第 ${i}/${steps} 次上报: 进度 ${currentTime}秒 / ${totalDuration}秒, 完成=${isEnd}`);
    
    const response = await fetch('/ushm/edu/contentsViewAviProcessCheckSub.do', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
            ...(baseParams.csrfToken && { 'X-CSRF-TOKEN': baseParams.csrfToken })
        },
        credentials: 'include',
        body: formData
    });

    const text = await response.text();
    console.log(`   响应: ${text.substring(0, 100)}`);

    const delay = 2000 + Math.floor(Math.random() * 3000);
    await new Promise(r => setTimeout(r, delay));
}

// ---------- 5. 最终完成标记(确保 isEnd=true 发送一次) ----------
console.log('发送最终完成确认...');
const finalParams = new URLSearchParams();
finalParams.append('scheduleMemberProgressNo', scheduleMemberProgressNo);
finalParams.append('currentTime', totalDuration);
finalParams.append('isEnd', 'true');
finalParams.append('isMobile', 'false');
Object.entries(baseParams).forEach(([k, v]) => { if (!finalParams.has(k)) finalParams.append(k, v); });

await fetch('/ushm/edu/contentsViewAviProcessCheckSub.do', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' },
    credentials: 'include',
    body: finalParams
});

console.log('%c The simulation of the learning process has completed! Refresh the page to check if the progress has reached 100%.', 'color: green; font-weight: bold; font-size: 14px');
alert('The script has finished running. Please refresh the page to see the results.');

})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment