Welcome to Part 3 of
How to write apps for
macOS/OSX in Perl! This app protects
Internet Privacy
by regularly changing DNS servers. It's designed to run constantly
in the background. I use it all day every day for the past 2 months.
This edition demonstrates how to:
- Write a very useful application!
- Use Perl to create, read and write a config file.
- Easily edit the config file.
- Configure multiple run time variables.
- Pass variables between Applescript and Perl.
- Handle errors and bad input.
- Use core Perl modules.
See Part 1 to get started with Perl and the built-in Mac
devtool Automator,
and the demo Perl app for Mac: Perl ASN Check
See Part 2 for more techniques to integrate
Perl into Mac with Applescript,
and the demo Perl app for Mac: Perl Version Tool
This ~150 liner is ~120 lines of Applescript
GUI logic linked to ~30 lines of core Perl code in the form of 3
one-liners! Hopefully our Mac-centric monks will pick up these techniques
to write and share Mac apps to improve computing experiences with Perl!
Remember: All Macs Have Perl!
Compile this code with the instructions in Part 1
to produce a 1.3MB portable binary application:
Source:
-- Set random DNS server every n minutes.
# Demonstration Apple macOS/OSX app in AppleScript and Perl
# Posted to perlmonks.org by Anonymous Monk 6/16/2018
# Node: How to write apps for macOS/OSX in Perl! Part 3: Random DNS Se
+rver
-- Part 1: Perl ASN Check https://perlmonks.org/?node_id=1216610
-- Part 2: Perl Version Tool https://perlmonks.org/?node_id=1216670
# DEFAULT DNS SERVERS:
# 1.1.1.1 = Cloudflare
# 8.8.8.8 = Google
# 45.77.165.194 = Fourth Estate Zero Knowledge
set DNS to "1.1.1.1
8.8.8.8
45.77.165.194"
set DEFAULT_NETWORK to "Wi-Fi"
set TITLE to "Random DNS Server"
# CREATE AND/OR READ CONFIGURATION FILE:
# 1. PASS APPLESCRIPT VARIABLES TO PERL ->
# 2. SEND PERL VARIABLES TO APPLESCRIPT <-
# 3. AND THAT LAST LINE...
try
set INI to do shell script "printf $HOME" & "/.dns.random.config"
set CFG to do shell script "
perl -Mautodie -we '
my $config = qq~" & INI & "~;
if (-e $config) {
open my $fh, q~<~, $config;
@_ = <$fh>; close $fh;
@_ = grep /\\S+/, @_;
print @_;
} else {
open my $fh, q~>~, $config;
print $fh qq~" & DNS & "~; close $fh;
print qq~" & DNS & "~;
}
'
"
on error oops
display alert oops as critical
end try
set DNS to CFG
# RUNTIME CONFIG LOOP
repeat
try
set TXT to "DNS Servers:
" & DNS & "
Minutes between change? (blank to exit)"
set EAE to "EXIT AND EDIT CONFIG"
# GET DIALOG OBJECT CONTAINING INPUT AND CLICKED BUTTON VALUE
set DUR to display dialog TXT with title TITLE default answer
+"" buttons {EAE, "OK"} default button 2
set DUR_text to text returned of DUR as number
set DUR_button to button returned of DUR
if DUR_button is EAE then # EDIT CONFIG
try
do shell script "open -a TextEdit " & INI & ""
return # EXIT
on error oops
display alert oops as critical
return # EXIT
end try
end if
if DUR_text is 0 then return # EXIT
set DUR to DUR_text
set NETS to do shell script "networksetup -listallnetworkservi
+ces"
set TXT to "Network Interfaces:
" & NETS & "
Network?"
set NETWORK to text returned of (display dialog TXT with title
+ TITLE default answer DEFAULT_NETWORK buttons {"OK"} default button 1
+)
try
# DOES NETWORK EXIST?
set hmm to do shell script "
perl -we '
@_ = qx/networksetup -getinfo " & NETWORK & "/;
$_ = join qq~\\n~, @_;
print /Error/ ? 0 : 1;
'
"
on error oops
display alert oops as critical
return # EXIT
end try
if hmm as number is equal to 0 then
display notification "Network not found! Exit..." with tit
+le TITLE
return # EXIT
end if
exit repeat # EXIT CONFIG LOOP
on error oops
display notification "This shouldn't happen!" with title TITLE
return # EXIT
end try
end repeat # END CONFIG LOOP
set MSG to button returned of (display dialog "Notification of change?
+" buttons {"No", "Yes"} default button 2)
# END CONFIG
# MAIN EVENT LOOP
repeat
try
# Use perl to read last line of resolv.conf as current DNS ser
+ver.
# Exclude current server and shuffle list to get new value.
# Set new server and return the old and new values to applescr
+ipt.
set PERL to do shell script "
perl -MList::Util=shuffle -Mautodie -we '
open my $fh, q~<~, q~/private/etc/resolv.conf~;
chomp(@_ = <$fh>);
close $fh;
$_ = pop @_;
my (undef,$cur) = split q~ ~;
$_ = qq~" & DNS & "~;
@_ = split /\\s+/;
@_ = grep !/$cur/, @_;
@_ = shuffle @_;
my $new = pop @_;
system(qq~networksetup -setdnsservers " & NETWORK & "
+$new~);
print qq~$cur $new~;
'
"
on error oops
display alert oops as critical
end try
# AN APPLESCRIPT SPLIT
set text item delimiters to {" "}
set {CUR, NEW} to text items 1 thru 2 of PERL
if MSG is "Yes" then
display notification "DNS changed from " & CUR & " to " & NEW
+with title TITLE
end if
delay ((DUR as integer) * 60)
end repeat
# MADE IN USA (This program, Perl, Apple, Me!)
# b9ce5dcd671f9647fb86a6f3709a572ffd6e2aa490c005300585a555fabf9ce8
# 060c38ad8715a6a2381cc653ad5a7dd1815f3cf990c31594b4a1b20ef4fc9d27
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.