Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ jobs:
- name: Fat-jar derle + test
run: mvn -B -ntp clean verify

- name: Installer araclari (best-effort)
continue-on-error: true
shell: bash
run: |
if [ "${{ runner.os }}" = "Windows" ]; then choco install wixtoolset -y --no-progress || true; fi
if [ "${{ runner.os }}" = "Linux" ]; then sudo apt-get update && sudo apt-get install -y fakeroot || true; fi

- name: jpackage app-image (JRE gomulu)
shell: bash
run: |
Expand All @@ -53,16 +60,48 @@ jobs:
zip -r "../AutoClicker-${{ runner.os }}.zip" AutoClicker* >/dev/null
fi

- name: Release'e OS paketini yukle
uses: softprops/action-gh-release@v2
with:
prerelease: true
generate_release_notes: true
files: AutoClicker-${{ runner.os }}.zip
- name: jpackage native installer (msi/dmg/deb) - best-effort
id: installer
continue-on-error: true
shell: bash
run: |
case "${{ runner.os }}" in
Windows) T=msi ;;
macOS) T=dmg ;;
Linux) T=deb ;;
esac
jpackage --type "$T" --input target --main-jar autoclicker.jar \
--name AutoClicker --app-version "${{ steps.ver.outputs.v }}" --dest installer \
--java-options "-Dfile.encoding=UTF-8"

- name: Release'e fat-jar yukle (yalnizca Linux)
if: runner.os == 'Linux'
- name: Yayim dosyalari + SHA-256 checksum
shell: bash
run: |
mkdir -p out
cp "AutoClicker-${{ runner.os }}.zip" out/ 2>/dev/null || true
if [ -d installer ]; then cp installer/* out/ 2>/dev/null || true; fi
if [ "${{ runner.os }}" = "Linux" ]; then cp target/autoclicker.jar out/ 2>/dev/null || true; fi
cd out
for f in *; do
[ -f "$f" ] || continue
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$f" > "$f.sha256"; else shasum -a 256 "$f" > "$f.sha256"; fi
done
ls -la

# ---- KOD IMZALAMA (opsiyonel - SERTIFIKA gerektirir) ----
# Ucretsiz yol: yukaridaki SHA-256 checksum'lar butunluk dogrulamasi saglar (imza degil).
# Imzalamak icin repo Settings > Secrets'a sertifikalari ekleyip asagidaki adimlari etkinlestirin:
# Windows: WIN_CERT_BASE64 (.pfx -> base64), WIN_CERT_PASSWORD
# - name: Windows imzala (jsign)
# if: runner.os == 'Windows'
# shell: bash
# run: |
# echo "${{ secrets.WIN_CERT_BASE64 }}" | base64 -d > cert.pfx
# for f in out/*.msi; do jsign --storetype PKCS12 --keystore cert.pfx --storepass "${{ secrets.WIN_CERT_PASSWORD }}" "$f"; done
# macOS: APPLE_DEV_ID sertifikasi import + codesign + 'xcrun notarytool submit --wait' ile imzala/notarize.
- name: Release'e yukle
uses: softprops/action-gh-release@v2
with:
prerelease: true
files: target/autoclicker.jar
generate_release_notes: true
files: out/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ dist/
*.properties
# Maven wrapper properties'i *.properties'e ragmen izlenmeli
!.mvn/wrapper/maven-wrapper.properties
# i18n ceviri kaynaklari *.properties'e ragmen izlenmeli
!src/main/resources/i18n/messages*.properties

# ────────────────────────────────────────
# IDE dosyaları
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@ com.ohualtex.autoclicker

---

## 🔒 Güvenlik & Doğrulama

Release dosyaları **kod imzalı değildir** (ücretsiz dağıtım); bunun yerine her dosya için **SHA-256 checksum** (`*.sha256`) yayınlanır.

**Bütünlüğü doğrula:**
```bash
# Linux/macOS
shasum -a 256 -c AutoClicker-Linux.zip.sha256
# Windows (PowerShell)
(Get-FileHash AutoClicker-Windows.zip -Algorithm SHA256).Hash
```

**İşletim sistemi uyarısını aşma** (imzasız olduğu için normaldir):
- **Windows SmartScreen:** *"Daha fazla bilgi" → "Yine de çalıştır"*
- **macOS Gatekeeper:** *Sistem Ayarları → Gizlilik ve Güvenlik → "Yine de Aç"*, veya `xattr -dr com.apple.quarantine AutoClicker.app`

İlk çalıştırmada uygulama bir **sorumlu-kullanım onayı** ister ve **GitHub'dan yeni sürüm** olup olmadığını arka planda kontrol eder (config'de `checkUpdates=false` ile kapatılabilir).

---

## 📌 Notlar & Uyumluluk

- **Ayar konumu:** Ayarlar `%APPDATA%\AutoClicker\config.properties` (Windows) veya `~/.autoclicker/` (macOS/Linux) altında saklanır; installer ile kurulumda bile yazılabilir.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.ohualtex</groupId>
<artifactId>autoclicker</artifactId>
<version>7.5</version>
<version>8.5</version>
<packaging>jar</packaging>

<name>AutoClicker Ultimate</name>
Expand Down
129 changes: 77 additions & 52 deletions src/main/java/com/ohualtex/autoclicker/AutoClicker.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.ohualtex.autoclicker.model.ActionType;
import com.ohualtex.autoclicker.model.MacroAction;
import com.ohualtex.autoclicker.ui.Icons;
import com.ohualtex.autoclicker.ui.Widgets;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
Expand Down Expand Up @@ -149,6 +150,66 @@ public void windowClosing(WindowEvent e) {
saveConfig();
}
});

checkConsent(); // ilk calistirmada sorumlu-kullanim onayi (kabul edilmezse cikar)
checkForUpdate(); // arka planda surum kontrolu (best-effort, sessiz)
}

// Ilk calistirmada sorumlu-kullanim/yasal sorumluluk reddi onayi; bir kez gosterilir
private void checkConsent() {
if (Boolean.parseBoolean(props.getProperty("consentAccepted", "false"))) return;
int r = JOptionPane.showConfirmDialog(this, Lang.get("consent_msg"), Lang.get("consent_title"),
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
if (r != JOptionPane.OK_OPTION) { System.exit(0); }
props.setProperty("consentAccepted", "true");
saveConfig();
}

// GitHub Releases'i kontrol eder; daha yeni surum varsa kullaniciya bildirir (arka plan, best-effort)
private void checkForUpdate() {
if (!Boolean.parseBoolean(props.getProperty("checkUpdates", "true"))) return;
Thread t = new Thread(() -> {
try {
java.net.http.HttpClient c = java.net.http.HttpClient.newHttpClient();
java.net.http.HttpRequest req = java.net.http.HttpRequest.newBuilder()
.uri(java.net.URI.create("https://api.github.com/repos/Ohualtex/Auto_Clicker/releases/latest"))
.header("Accept", "application/vnd.github+json")
.timeout(java.time.Duration.ofSeconds(6)).build();
String body = c.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body();
String tag = new com.google.gson.JsonParser().parse(body).getAsJsonObject().get("tag_name").getAsString();
String latest = tag.startsWith("v") ? tag.substring(1) : tag;
if (isNewer(latest, appVersion())) {
SwingUtilities.invokeLater(() -> {
int r = JOptionPane.showConfirmDialog(this,
String.format(Lang.get("update_msg"), latest, appVersion()),
Lang.get("update_title"), JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE);
if (r == JOptionPane.YES_OPTION) openReleasesPage();
});
}
} catch (Exception ignore) { /* ag/parse hatasi: sessiz */ }
}, "AutoClicker-UpdateCheck");
t.setDaemon(true);
t.start();
}

// "8.10" > "8.9" gibi sayisal-parcali surum karsilastirmasi
static boolean isNewer(String latest, String current) {
try {
String[] a = latest.split("\\."), b = current.split("\\.");
int n = Math.max(a.length, b.length);
for (int i = 0; i < n; i++) {
int x = i < a.length ? Integer.parseInt(a[i].trim()) : 0;
int y = i < b.length ? Integer.parseInt(b[i].trim()) : 0;
if (x != y) return x > y;
}
return false;
} catch (Exception e) { return false; }
}

private void openReleasesPage() {
try {
Desktop.getDesktop().browse(java.net.URI.create("https://github.com/Ohualtex/Auto_Clicker/releases/latest"));
} catch (Exception ignore) {}
}

// Sistem tepsisi ikonu: simge durumuna kuculunce gizle, tray menusunden goster/baslat-durdur/cikis
Expand Down Expand Up @@ -226,15 +287,7 @@ private void rebuildUI() {
}

private JButton createInfoButton(String tooltipKey) {
JButton btn = new JButton(new Icons.InfoIcon());
btn.setMargin(new Insets(0,0,0,0));
btn.setBorderPainted(false);
btn.setContentAreaFilled(false);
btn.setFocusPainted(false);
btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
btn.setToolTipText("<html><p width=\"250\">" + Lang.get(tooltipKey) + "</p></html>");
btn.addActionListener(e -> JOptionPane.showMessageDialog(this, Lang.get(tooltipKey), Lang.get("info_title"), JOptionPane.INFORMATION_MESSAGE));
return btn;
return Widgets.infoButton(this, tooltipKey);
}

private void applyColorsRecursively(Component c, Color color) {
Expand Down Expand Up @@ -882,11 +935,14 @@ public void keyPressed(KeyEvent ev) {
int t = typeBox.getSelectedIndex();
if(t==0) {
int mIdx = mouseBox.getSelectedIndex();
int mask = InputEvent.BUTTON1_DOWN_MASK;
if(mIdx==1) mask = InputEvent.BUTTON3_DOWN_MASK;
else if(mIdx==2) mask = InputEvent.BUTTON2_DOWN_MASK;
else if(mIdx==3) mask = MacroAction.DOUBLE_CLICK;
chainModel.addElement(new MacroAction(ActionType.MOUSE_CLICK, mask, 0));
if (mIdx == 3) {
chainModel.addElement(new MacroAction(ActionType.MOUSE_DOUBLE_CLICK, 0, 0));
} else {
int mask = InputEvent.BUTTON1_DOWN_MASK;
if(mIdx==1) mask = InputEvent.BUTTON3_DOWN_MASK;
else if(mIdx==2) mask = InputEvent.BUTTON2_DOWN_MASK;
chainModel.addElement(new MacroAction(ActionType.MOUSE_CLICK, mask, 0));
}
} else if(t==1) {
chainModel.addElement(new MacroAction(ActionType.KEY_PRESS, selectedActKey[0], 0));
} else if(t==2) {
Expand Down Expand Up @@ -1088,37 +1144,7 @@ private JPanel buildSettingsPanel() {
}

private JPanel createCpsPanel(String title, String defaultVal, int max, String infoKey, java.util.function.Consumer<JSlider> setSlider) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createTitledBorder(title));

JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JSlider slider = new JSlider(1, max, Integer.parseInt(defaultVal));
JTextField field = new JTextField(defaultVal, 5);

slider.addChangeListener(e -> field.setText(String.valueOf(slider.getValue())));
field.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
boolean ok = false;
try {
int val = Integer.parseInt(field.getText().trim());
if (val >= slider.getMinimum() && val <= max) { slider.setValue(val); ok = true; }
} catch (Exception ex) { }
// Bos alan notr; gecersiz/araliga sigmayan deger kirmizi cerceve
field.putClientProperty("JComponent.outline", (ok || field.getText().trim().isEmpty()) ? null : "error");
field.repaint();
}
});

setSlider.accept(slider);

inputPanel.add(slider);
inputPanel.add(field);
if (infoKey != null) {
inputPanel.add(createInfoButton(infoKey));
}
panel.add(inputPanel);
return panel;
return Widgets.cpsPanel(this, title, defaultVal, max, infoKey, setSlider);
}

private void initJNativeHook() {
Expand Down Expand Up @@ -1437,13 +1463,12 @@ private boolean startChainMacro() {
SwingUtilities.invokeLater(() -> chainList.setSelectedIndex(idx));
switch (action.type) {
case MOUSE_CLICK:
if (action.p1 == MacroAction.DOUBLE_CLICK) {
doMouseClick(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(40);
doMouseClick(InputEvent.BUTTON1_DOWN_MASK);
} else {
doMouseClick(action.p1);
}
doMouseClick(action.p1);
break;
case MOUSE_DOUBLE_CLICK:
doMouseClick(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(40);
doMouseClick(InputEvent.BUTTON1_DOWN_MASK);
break;
case KEY_PRESS:
robot.keyPress(action.p1);
Expand Down
Loading
Loading